Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief ATMI lib part for XA api
0003  *   Responsible for:
0004  *   - Loading the drivers in app.
0005  *   Think about automatic open...
0006  *   RECON: We expect that on first network failure, XA APIs should return XAER_RMFAIL
0007  *   on such error, if RECON is configured we shall re-establish connection.
0008  *   In case if last call (if counter is exceeded) still we are getting XAER_RMFAIL
0009  *   keep the internal status that connection is broken and on next API call we firstly
0010  *   try to connect to resource, and only the proceed. If proceed fails, attempt
0011  *   counter continues to count from first attempts (if there was any).
0012  *
0013  * @file xa.c
0014  */
0015 /* -----------------------------------------------------------------------------
0016  * Enduro/X Middleware Platform for Distributed Transaction Processing
0017  * Copyright (C) 2009-2016, ATR Baltic, Ltd. All Rights Reserved.
0018  * Copyright (C) 2017-2023, Mavimax, Ltd. All Rights Reserved.
0019  * This software is released under one of the following licenses:
0020  * AGPL (with Java and Go exceptions) or Mavimax's license for commercial use.
0021  * See LICENSE file for full text.
0022  * -----------------------------------------------------------------------------
0023  * AGPL license:
0024  *
0025  * This program is free software; you can redistribute it and/or modify it under
0026  * the terms of the GNU Affero General Public License, version 3 as published
0027  * by the Free Software Foundation;
0028  *
0029  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0030  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0031  * PARTICULAR PURPOSE. See the GNU Affero General Public License, version 3
0032  * for more details.
0033  *
0034  * You should have received a copy of the GNU Affero General Public License along 
0035  * with this program; if not, write to the Free Software Foundation, Inc.,
0036  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0037  *
0038  * -----------------------------------------------------------------------------
0039  * A commercial use license is available from Mavimax, Ltd
0040  * contact@mavimax.com
0041  * -----------------------------------------------------------------------------
0042  */
0043 #include <string.h>
0044 #include <stdio.h>
0045 #include <stdlib.h>
0046 #include <memory.h>
0047 #include <errno.h>
0048 #include <dlfcn.h>
0049 
0050 #include <atmi.h>
0051 #include <atmi_shm.h>
0052 #include <ndrstandard.h>
0053 #include <ndebug.h>
0054 #include <nstdutil.h>
0055 #include <ndrxdcmn.h>
0056 #include <userlog.h>
0057 
0058 /* shm_* stuff, and mmap() */
0059 #include <sys/mman.h>
0060 #include <sys/types.h>
0061 /* exit() etc */
0062 #include <unistd.h>
0063 #include <fcntl.h>
0064 #include <sys/stat.h>
0065 #include <sys/ipc.h>
0066 #include <xa_cmn.h>
0067 #include <tperror.h>
0068 #include <atmi_tls.h>
0069 #include "Exfields.h"
0070 #include "sys_test.h"
0071 /*---------------------------Externs------------------------------------*/
0072 /*---------------------------Macros-------------------------------------*/
0073 
0074 #define XA_API_ENTRY(X) {\
0075     ATMI_TLS_ENTRY;\
0076     if (!M_is_xa_init) { \
0077         if (EXSUCCEED!=(ret = atmi_xa_init()))\
0078         {\
0079             goto out;\
0080         }\
0081     }\
0082     if (!G_atmi_tls->M_is_curtx_init)\
0083     {\
0084         if (EXSUCCEED!=(ret=atmi_xa_init_thread(X)))\
0085         {\
0086             goto out;\
0087         }\
0088     }\
0089 }\
0090 
0091 /**
0092  * This is generic API retry engine. Uses global retry
0093  * counter with specified condition code to retry on.
0094  * this assumes that XA_OK is expected to be returned by API.
0095  * TODO: In case of protocl failures and restarts. We might want to track,
0096  * if we know that connection was open, then reloop on RMFAIL and do not touch XAER_PROTO?
0097  * Currently if we are in the API, we do not check are we open or not, thus have to relay on XAER_PROTO.
0098  * @param (call) expression to call on retry
0099  * @param (retry_condition) error condition on which to retry
0100  * @param (bad_status) expression indicating that if true, we are still failing
0101  * @param do_primary shall we call the primary expression?
0102  */
0103 #define GENERIC_RETRY_CORE(call, retry_condition, bad_status, do_primary) do {\
0104         /* Check that return code is specified or loop second attempt is it in global retry list */\
0105         if (G_atmi_env.xa_recon_times && (retry_condition))\
0106         {\
0107             if (do_primary)\
0108             {\
0109                 NDRX_LOG(log_warn, "RECON: Entry of %s() failed with %d", __func__, ret);\
0110             }\
0111             while (tries<G_atmi_env.xa_recon_times)\
0112             {\
0113                 tries++;\
0114                 NDRX_LOG(log_warn, "RECON: >>> Attempt %d type=%s. Sleeping %ld micro-sec", \
0115                         tries, (do_primary?__func__:"conn-only"), G_atmi_env.xa_recon_usleep);\
0116                 usleep(G_atmi_env.xa_recon_usleep);\
0117                 NDRX_LOG(log_warn, "RECON: Retrying...");\
0118                 /* xa_close */\
0119                 NDRX_LOG(log_warn, "RECON: atmi_xa_close_entry()");\
0120                 atmi_xa_close_entry(EXTRUE);\
0121                 NDRX_LOG(log_warn, "RECON: atmi_xa_open_entry()");\
0122                 /* keep the last error */\
0123                 ndrx_TPunset_error();\
0124                 if (XA_OK==(ret=atmi_xa_open_entry()))\
0125                 {\
0126                     /* restart... */\
0127                     NDRX_LOG(log_warn, "RECON: %s() call of atmi_xa_open_entry() OK", __func__);\
0128                     if (do_primary)\
0129                     {\
0130                         NDRX_LOG(log_warn, "RECON: Retry of %s()", __func__);\
0131                         /* rest the error code? previous errors keeps the original error code?*/\
0132                         ndrx_TPunset_error();\
0133                         ret = (call);\
0134                         if (!(bad_status))\
0135                         {\
0136                             NDRX_LOG(log_warn, "RECON: <<< Succeed (%s)", __func__);\
0137                             break;\
0138                         }\
0139                         else\
0140                         {\
0141                             if ((retry_condition))\
0142                             {\
0143                                 NDRX_LOG(log_warn, "RECON: <<< Attempt %d. %s() failed %d", \
0144                                     tries, __func__, ret);\
0145                             }\
0146                             else\
0147                             { /* this is different error, so not attempting... */\
0148                                 NDRX_LOG(log_warn, "RECON: <<< Attempt %d. %s() failed %d, no continue", \
0149                                     tries, __func__, ret);\
0150                                 break;\
0151                             }\
0152                         }\
0153                     }\
0154                     else\
0155                     {\
0156                         NDRX_LOG(log_warn, "RECON: <<< Succeed (connection)");\
0157                         break;\
0158                     }\
0159                 }\
0160                 else\
0161                 {\
0162                     NDRX_LOG(log_error, "RECON: <<< Attempt %d. atmi_xa_open_entry() - "\
0163                        "fail: %d [%s]", tries, ret, atmi_xa_geterrstr(ret));\
0164                 }\
0165             } /* for tries */\
0166             /* still failed, let next calls to survive */\
0167             if (XAER_RMFAIL==ret)\
0168             {\
0169                 atmi_xa_close_entry(EXTRUE);\
0170             }\
0171         } /* if retry supported. */\
0172     } while (0)
0173 
0174 
0175 /**
0176  * Generic retry engine for all others except start
0177  * see args of GENERIC_RETRY_CORE()
0178  */
0179 #define GENERIC_RETRY(call, retry_condition, bad_status) do {\
0180         GENERIC_RETRY_CORE((call), (retry_condition), (bad_status), EXTRUE);\
0181         if (bad_status)\
0182         {\
0183             NDRX_LOG(log_error, "finally %s - fail: %d [%s]", \
0184                     __func__, ret, atmi_xa_geterrstr(ret));\
0185             ndrx_TPset_error_fmt_rsn(TPERMERR,  \
0186                     ret, "finally %s - fail: %d [%s]", \
0187                     __func__, ret, atmi_xa_geterrstr(ret));\
0188             goto out;\
0189         }\
0190     } while (0)
0191 
0192 /**
0193  * Keep the common attempts counter as for entry connect and later reconnects
0194  * after bad primary method.
0195  */
0196 #define GENERIC_RETRY_ENTRY(do_rollback) \
0197         /* perform retry with common counter */\
0198         GENERIC_RETRY_CORE(0, G_atmi_tls->G_atmi_xa_curtx.is_xa_conn_error, 0, EXFALSE);\
0199         do\
0200         {\
0201             if (XA_OK!=ret)\
0202             {\
0203                 if (do_rollback)\
0204                 {\
0205                     ndrx_xa_join_fail(NULL, EXFALSE);\
0206                     atmi_xa_reset_curtx();\
0207                 }\
0208                 NDRX_LOG(log_error, "finally %s - fail: %d [%s]", \
0209                         __func__, ret, atmi_xa_geterrstr(ret));\
0210                 ndrx_TPset_error_fmt_rsn(TPERMERR,  \
0211                         ret, "finally %s - fail: %d [%s]", \
0212                         __func__, ret, atmi_xa_geterrstr(ret));\
0213                 goto out;\
0214             }\
0215         } while (0)
0216 
0217 /**
0218  * Generic define for retry engine
0219  * keeps the common re-connect attempts counter!
0220  */
0221 #define GENERIC_RETRY_DEF \
0222     int tries = 0
0223 
0224 /*---------------------------Enums--------------------------------------*/
0225 /*---------------------------Typedefs-----------------------------------*/
0226 /*---------------------------Globals------------------------------------*/
0227 /*---------------------------Statics------------------------------------*/
0228 
0229 /** current library status */
0230 exprivate int volatile M_is_xa_init = EXFALSE;
0231 
0232 /** protect from double init... */
0233 exprivate MUTEX_LOCKDECL(M_is_xa_init_lock);
0234 
0235 /*---------------------------Prototypes---------------------------------*/
0236 exprivate int atmi_xa_init_thread(int do_open);
0237 exprivate int ndrx_xa_join_fail(int *did_abort, int force_abort);
0238 
0239 /******************************************************************************/
0240 /*                          LIB INIT                                          */
0241 /******************************************************************************/
0242 
0243 /**
0244  * Return Enduro/X null switch (special case for aix/golang) - to
0245  * have atlest null switch available - thus provide internal one
0246  * @return XA switch or null
0247  */
0248 exprivate struct xa_switch_t *ndrx_aix_fix(void)
0249 {
0250     return &tmnull_switch;
0251 }
0252 
0253 /**
0254  * Initialize current thread
0255  */
0256 exprivate int atmi_xa_init_thread(int do_open)
0257 {
0258     int ret = EXSUCCEED;
0259     
0260     /* ATMI_TLS_ENTRY; - not needed called already from macros which does the init */
0261     
0262     memset(&G_atmi_tls->G_atmi_xa_curtx, 0, sizeof(G_atmi_tls->G_atmi_xa_curtx));
0263     G_atmi_tls->M_is_curtx_init = EXTRUE;
0264     
0265 out:
0266     return ret;
0267 }
0268 /**
0269  * Un-initialize XA lib (for thread)
0270  * @return 
0271  */
0272 expublic void atmi_xa_uninit(void)
0273 {
0274     ATMI_TLS_ENTRY;
0275     /* do only thread based stuff un-init */
0276     if (G_atmi_tls->M_is_curtx_init)
0277     {
0278         if (G_atmi_tls->G_atmi_xa_curtx.is_xa_open)
0279         {
0280             atmi_xa_close_entry(EXFALSE);
0281             G_atmi_tls->G_atmi_xa_curtx.is_xa_open = EXFALSE;
0282         }
0283         G_atmi_tls->M_is_curtx_init = EXFALSE;
0284     }
0285 }
0286 
0287 /**
0288  * Initialize the XA drivers
0289  * we should load the Enduro/X driver for target XA resource manager 
0290  * and get handler for XA api.
0291  *
0292  * @return 
0293  */
0294 expublic int atmi_xa_init(void)
0295 {
0296     int ret=EXSUCCEED;
0297     void *handle; /* keep the handle, so that we have a reference */
0298     ndrx_get_xa_switch_loader func;
0299     char *error;
0300     char *xa_flags = NULL; /* dynamically allocated... */
0301     int has_lock = EXFALSE;
0302     
0303     /* Bug #565 avoid double init */
0304     if (!M_is_xa_init)
0305     {   
0306         MUTEX_LOCK_V(M_is_xa_init_lock);
0307         has_lock=EXTRUE;
0308         
0309         /* we are done... (other thread already performed init) */
0310         if (M_is_xa_init)
0311         {
0312             goto out;
0313         }
0314     }
0315     
0316     /* how about thread safety? */
0317     NDRX_LOG(log_info, "Loading XA driver: [%s]", G_atmi_env.xa_driverlib);
0318     handle = dlopen (G_atmi_env.xa_driverlib, RTLD_NOW);
0319     if (!handle)
0320     {
0321         error = dlerror();
0322         NDRX_LOG(log_error, "Failed to load XA lib [%s]: %s", 
0323                 G_atmi_env.xa_driverlib, error?error:"no dlerror provided");
0324 
0325         ndrx_TPset_error_fmt(TPEOS, "Failed to load XA lib [%s]: %s", 
0326                 G_atmi_env.xa_driverlib, error?error:"no dlerror provided");
0327         EXFAIL_OUT(ret);
0328     }
0329 
0330     func = (ndrx_get_xa_switch_loader)dlsym(handle, "ndrx_get_xa_switch");
0331 
0332 /* for golang brtl runtime linkage cannot be enabled, thus processes
0333  * do no see the global Enduro/X variables,
0334  * we can make special exception here for nullswitch, to use
0335  * built-in symbol here
0336  */
0337 #ifdef EX_OS_AIX
0338     if (ndrx_str_ends_with(G_atmi_env.xa_driverlib, "libndrxxanulls.so"))
0339     {
0340         func = ndrx_aix_fix;
0341     }
0342 #endif
0343 
0344     if (!func) 
0345     {
0346 
0347         error = dlerror();
0348         NDRX_LOG(log_error, "Failed to get symbol `ndrx_get_xa_switch' [%s]: %s", 
0349             G_atmi_env.xa_driverlib, error?error:"no dlerror provided");
0350 
0351         ndrx_TPset_error_fmt(TPESYSTEM, "Failed to get symbol `ndrx_get_xa_switch' [%s]: %s", 
0352             G_atmi_env.xa_driverlib, error?error:"no dlerror provided");
0353         EXFAIL_OUT(ret);
0354     }
0355 
0356     NDRX_LOG(log_info, "About to call ndrx_get_xa_switch()");
0357 
0358     /* Do not deallocate the lib... */
0359     if (NULL==(G_atmi_env.xa_sw = func()))
0360     {
0361         NDRX_LOG(log_error, "Cannot get XA switch handler - "
0362                         "`ndrx_get_xa_switch()' - returns NULL");
0363 
0364         ndrx_TPset_error_fmt(TPESYSTEM,  "Cannot get XA switch handler - "
0365                         "`ndrx_get_xa_switch()' - returns NULL");
0366         EXFAIL_OUT(ret);
0367     }
0368 
0369     NDRX_LOG(log_info, "Using XA %s", 
0370             (G_atmi_env.xa_sw->flags&TMREGISTER)?"dynamic registration":"static registration");
0371 
0372     /* why ?
0373      * still we can suspend / resume and join even TMNOMIGRATE is set
0374     if (G_atmi_env.xa_sw->flags & TMNOMIGRATE)
0375     {
0376         NDRX_LOG(log_warn, "XA Switch has TMNOMIGRATE flag -> fallback to nojoin");
0377         ndrx_xa_nojoin(EXTRUE);
0378     }
0379      */
0380 
0381     /* Parse the flags... and store the config 
0382      * This is done for Feature #160. Customer have an issue with xa_start
0383      * after a while. Suspect that firewall closes connections and oracle XA
0384      * lib looses the connection (fact that there was xa_open()). Thus 
0385      * at xa_start allow to retry with xa_close(), xa_open() and xa_start.
0386      */
0387     NDRX_LOG(log_debug, "xa_flags = [%s]", G_atmi_env.xa_flags);
0388     G_atmi_env.xa_fsync_flags=0;
0389     
0390     /* default other retry is XAER_RMFAIL */
0391     NDRX_STRCPY_SAFE(G_atmi_env.xa_recon_retcodes_other, ",-7,");
0392     if (EXEOS!=G_atmi_env.xa_flags[0])
0393     {
0394         char *tag_ptr;
0395         /* Note this will be parsed and EOS inserted. */
0396         char *tag_first;
0397         char *tag_token;
0398         int token_nr = 0;
0399 
0400         char *value_ptr, *value_first, *value_token;
0401 
0402         if (NULL==(xa_flags = NDRX_STRDUP(G_atmi_env.xa_flags)))
0403         {
0404             int err = errno;
0405             ndrx_TPset_error_fmt(TPEOS,  "Failed to allocate xa_flags temp buffer: %s", 
0406                     strerror(err));
0407 
0408             userlog("Failed to allocate xa_flags temp buffer: %s", strerror(err));
0409 
0410             EXFAIL_OUT(ret);
0411         }
0412 
0413         tag_first = xa_flags;
0414         NDRX_LOG(log_debug, "About token: [%s]", tag_first);
0415         while ((tag_token = strtok_r(tag_first, ";", &tag_ptr)))
0416         {
0417             if (NULL!=tag_first)
0418             {
0419                 tag_first = NULL; /* now loop over the string */
0420             }
0421 
0422             NDRX_LOG(log_debug, "Got tag [%s]", tag_token);
0423 
0424             /* format: RECON:<1,2,* - error codes>:<tries>:<sleep_millisec>
0425              * "*" - used for any error.
0426              * example:
0427              * RECON:*:3:250
0428              * Meaning: on any xa_start error, reconnect (tpclose/tpopen/tpbegin)
0429              * 3x times, between attempts sleep 250ms.
0430              */
0431             if (0==strncmp(tag_token, NDRX_XA_FLAG_RECON_TEST, strlen(NDRX_XA_FLAG_RECON_TEST)))
0432             {
0433                 value_first = tag_token;
0434                 G_atmi_env.xa_recon_usleep = EXFAIL;
0435                 NDRX_LOG(log_warn, "Parsing RECON tag... [%s]", value_first);
0436 
0437                 while ((value_token = strtok_r(value_first, ":", &value_ptr)))
0438                 {
0439                     token_nr++;
0440                     if (NULL!=value_first)
0441                     {
0442                         value_first = NULL; /* now loop over the string */
0443                     }
0444 
0445                     switch (token_nr)
0446                     {
0447                         case 1:
0448                             /* This is "RECON" */
0449                             NDRX_LOG(log_debug, "RECON: 1: [%s]", value_token);
0450                             break;
0451                         case 2:
0452                             /* This is list of error codes */
0453                             NDRX_LOG(log_debug, "RECON: 2: [%s]", value_token);
0454                             snprintf(G_atmi_env.xa_recon_retcodes, 
0455                                     sizeof(G_atmi_env.xa_recon_retcodes),
0456                                     ",%s,", value_token);
0457 
0458                             /* Remove spaces and tabs.. */
0459                             ndrx_str_strip(G_atmi_env.xa_recon_retcodes, "\t ");
0460 
0461                             break;
0462 
0463                         case 3:
0464                             NDRX_LOG(log_debug, "RECON: 3: [%s]", value_token);
0465                             G_atmi_env.xa_recon_times = atoi(value_token);
0466                             break;
0467                         case 4:
0468                             /* so user gives us milliseconds */
0469                             NDRX_LOG(log_debug, "RECON: 4: [%s]", value_token);
0470                             G_atmi_env.xa_recon_usleep = atol(value_token)*1000;
0471                             break;
0472                         case 5:
0473                             /* This is list of error codes */
0474                             NDRX_LOG(log_debug, "RECON: 5: [%s]", value_token);
0475                             snprintf(G_atmi_env.xa_recon_retcodes_other, 
0476                                     sizeof(G_atmi_env.xa_recon_retcodes_other),
0477                                     ",%s,", value_token);
0478 
0479                             /* Remove spaces and tabs.. */
0480                             ndrx_str_strip(G_atmi_env.xa_recon_retcodes_other, "\t ");
0481 
0482                             break;
0483                     }
0484                 }
0485 
0486                 if (G_atmi_env.xa_recon_usleep < 0)
0487                 {
0488                     NDRX_LOG(log_error, "Invalid [%s] settings in "
0489                             "XA_FLAGS [%s] (usleep not set)", 
0490                             NDRX_XA_FLAG_RECON, G_atmi_env.xa_flags);
0491 
0492                     ndrx_TPset_error_fmt(TPEINVAL, "Invalid [%s] settings in "
0493                             "XA_FLAGS [%s] (usleep not set)", 
0494                             NDRX_XA_FLAG_RECON, G_atmi_env.xa_flags);
0495 
0496                     EXFAIL_OUT(ret);
0497                 }
0498 
0499                 NDRX_LOG(log_error, "XA flag: [%s]: on xa_start ret codes: [%s],"
0500                         " recon number of %d times, sleep %ld "
0501                         "microseconds between attempts",
0502                         NDRX_XA_FLAG_RECON, 
0503                         G_atmi_env.xa_recon_retcodes, 
0504                         G_atmi_env.xa_recon_times, 
0505                         G_atmi_env.xa_recon_usleep);
0506             } /* If tag is NOJOIN */
0507             else if (0==strcmp(tag_token, NDRX_XA_FLAG_NOJOIN))
0508             {
0509                 ndrx_xa_nojoin(EXTRUE);
0510             }
0511             else if (0==strcmp(tag_token, NDRX_XA_FLAG_NOSTARTXID))
0512             {
0513                 ndrx_xa_nostartxid(EXTRUE);
0514             }
0515             else if (0==strcmp(tag_token, NDRX_XA_FLAG_NOSUSPEND))
0516             {
0517                 ndrx_xa_nosuspend(EXTRUE);
0518             }
0519             else if (0==strcmp(tag_token, NDRX_XA_FLAG_FSYNC))
0520             {
0521                 NDRX_LOG(log_warn, "XA FSYNC flag found");
0522                 G_atmi_env.xa_fsync_flags|=NDRX_FSYNC_FSYNC;
0523             }
0524             else if (0==strcmp(tag_token, NDRX_XA_FLAG_FDATASYNC))
0525             {
0526                 NDRX_LOG(log_warn, "XA FDATASYNC flag found");
0527                 G_atmi_env.xa_fsync_flags|=NDRX_FSYNC_FDATASYNC;
0528             }
0529             else if (0==strcmp(tag_token, NDRX_XA_FLAG_DSYNC))
0530             {
0531                 NDRX_LOG(log_warn, "XA DSYNC flag found");
0532                 G_atmi_env.xa_fsync_flags|=NDRX_FSYNC_DSYNC;
0533             }
0534             else if (0==strcmp(tag_token, NDRX_XA_FLAG_BTIGHT))
0535             {
0536                 ndrx_xa_btight(EXTRUE);
0537             }
0538 
0539         } /* for tag.. */
0540     } /* if xa_flags set */
0541         
0542     M_is_xa_init = EXTRUE;
0543     
0544     if (EXSUCCEED==ret)
0545     {
0546         NDRX_LOG(log_info, "XA lib initialized.");
0547         /* M_is_xa_init = TRUE; */
0548     }
0549     
0550 out:
0551      
0552     if (has_lock)
0553     {
0554         MUTEX_UNLOCK_V(M_is_xa_init_lock);
0555     }
0556 
0557     if (NULL!=xa_flags)
0558     {
0559         NDRX_FREE(xa_flags);
0560     }
0561 
0562     if (EXSUCCEED!=ret && NULL!=handle)
0563     {
0564         /* close the handle */
0565         dlclose(handle);
0566     }
0567 
0568     return ret;
0569 }
0570 
0571 /******************************************************************************/
0572 /*                          XA ENTRY FUNCTIONS                                */
0573 /******************************************************************************/
0574 
0575 /**
0576  * Wrapper for `open_entry'
0577  * @return 
0578  */
0579 expublic int atmi_xa_open_entry(void)
0580 {
0581     int ret = EXSUCCEED;
0582     XA_API_ENTRY(EXFALSE); /* already does ATMI_TLS_ENTRY; */
0583     
0584     NDRX_LOG(log_debug, "atmi_xa_open_entry RMID=%hd", G_atmi_env.xa_rmid);
0585     
0586     if (G_atmi_tls->G_atmi_xa_curtx.is_xa_open 
0587             && !G_atmi_tls->G_atmi_xa_curtx.is_xa_conn_error)
0588     {
0589         NDRX_LOG(log_warn, "xa_open_entry already called for context!");
0590         goto out;
0591     }
0592     
0593     if (XA_OK!=(ret = G_atmi_env.xa_sw->xa_open_entry(G_atmi_env.xa_open_str, 
0594                                     G_atmi_env.xa_rmid, 0)))
0595     {
0596         /* required for retry engine, for ATMI it does not play big role here. */
0597         if (XAER_RMERR==ret)
0598         {
0599             ret = XAER_RMFAIL;
0600             NDRX_LOG(log_error, "atmi_xa_open_entry ret XAER_RMERR remapping to XAER_RMFAIL");
0601         }
0602 
0603         NDRX_LOG(log_error, "atmi_xa_open_entry - fail: %d [%s]", 
0604                 ret, atmi_xa_geterrstr(ret));
0605         
0606         /* we should  generate atmi error */
0607         ndrx_TPset_error_fmt_rsn(TPERMERR,  ret, "atmi_xa_open_entry - fail: %d [%s]", 
0608                 ret, atmi_xa_geterrstr(ret));
0609         
0610         goto out;
0611     }
0612     
0613     G_atmi_tls->G_atmi_xa_curtx.is_xa_open = EXTRUE;
0614     
0615     /* flag is set only if processing recon */
0616     if (G_atmi_tls->G_atmi_xa_curtx.is_xa_conn_error)
0617     {
0618         NDRX_LOG(log_warn, "RECON: Marking resource connection as OK");
0619         G_atmi_tls->G_atmi_xa_curtx.is_xa_conn_error = EXFALSE;
0620     }
0621     
0622     NDRX_LOG(log_info, "XA interface open");
0623     
0624 out:
0625     return ret;
0626 }
0627 
0628 /**
0629  * Wrapper for `close_entry'
0630  * TODO: Maybe we do not need XA_API_ENTRY here...
0631  * @param for_retry doing close for retry / error attempt
0632  * @return XA error
0633  */
0634 expublic int atmi_xa_close_entry(int for_retry)
0635 {
0636     int ret = EXSUCCEED;
0637     XA_API_ENTRY(EXTRUE); /* already does ATMI_TLS_ENTRY */
0638     
0639     NDRX_LOG(log_debug, "atmi_xa_close_entry");
0640     
0641     if (!G_atmi_tls->G_atmi_xa_curtx.is_xa_open)
0642     {
0643         NDRX_LOG(log_warn, "xa_close_entry already called for context!");
0644         goto out;
0645     }
0646     
0647     /* lets assume it is closed... */
0648     if (for_retry)
0649     {
0650         NDRX_LOG(log_warn, "RECON: Marking resource connection as ERROR");
0651         G_atmi_tls->G_atmi_xa_curtx.is_xa_conn_error = EXTRUE;
0652     }
0653     else
0654     {
0655         G_atmi_tls->G_atmi_xa_curtx.is_xa_open = EXFALSE;
0656     
0657     if (G_atmi_tls->G_atmi_xa_curtx.is_xa_conn_error)
0658     {
0659             NDRX_LOG(log_warn, "RECON: Resource connection was marked as ERROR. "
0660                     "Normal close, clearing flag");
0661             G_atmi_tls->G_atmi_xa_curtx.is_xa_conn_error = EXFALSE;
0662     }
0663     }
0664     
0665     if (XA_OK!=(ret = G_atmi_env.xa_sw->xa_close_entry(G_atmi_env.xa_close_str, 
0666                                     G_atmi_env.xa_rmid, 0)))
0667     {
0668         NDRX_LOG(log_error, "atmi_xa_close_entry - fail: %d [%s]", 
0669                 ret, atmi_xa_geterrstr(ret));
0670         
0671         if (!for_retry)
0672         {
0673             /* we should  generate atmi error */
0674             ndrx_TPset_error_fmt_rsn(TPERMERR,  ret, "atmi_xa_close_entry - fail: %d [%s]", 
0675                     ret, atmi_xa_geterrstr(ret));
0676         }
0677         goto out;
0678     }
0679     
0680 out:
0681     return ret;
0682 }
0683 
0684 /**
0685  * Test the RECON settings for error 
0686  * @param list error code checking list
0687  * @param retcode return code for xa_start to test for
0688  * @return TRUE - do retry, FALSE - no retry
0689  */
0690 exprivate int is_error_in_recon_list(char *list, int retcode)
0691 {
0692     char scanstr[16];
0693     char scanstr2[4] = ",*,";
0694     int ret = EXFALSE;
0695     
0696     snprintf(scanstr, sizeof(scanstr), ",%d,", retcode);
0697     
0698     NDRX_LOG(log_warn, "%s testing return code [%s] in recon list [%s]", 
0699             __func__, scanstr, list);
0700     
0701     if (NULL!=strstr(list, scanstr))
0702     {
0703         NDRX_LOG(log_warn, "matched by code - DO RETRY");
0704         ret = EXTRUE;
0705         goto out;
0706     }
0707     else if (NULL!=strstr(list, scanstr2))
0708     {
0709         NDRX_LOG(log_warn, "matched by wildcard - DO RETRY");
0710         ret = EXTRUE;
0711         goto out;
0712     }
0713     
0714 out:
0715     return ret;
0716     
0717 }
0718 /**
0719  * Start transaction (or join..) depending on flags.
0720  * @param xid
0721  * @param flags
0722  * @param silent_err for XAER_NOTA or XAER_DUPID errors, do not generate errors in log
0723  * @return EXSUCCEED/EXFAIL
0724  */
0725 expublic int atmi_xa_start_entry(XID *xid, long flags, int silent_err)
0726 {
0727     int ret = EXSUCCEED;
0728     int need_retry;
0729     GENERIC_RETRY_DEF;
0730     XA_API_ENTRY(EXTRUE);
0731     
0732     NDRX_LOG(log_debug, "%s", __func__);
0733     
0734     /* Generic Retry entry */
0735     GENERIC_RETRY_ENTRY(EXFALSE);
0736     
0737     if (XA_OK!=(ret = G_atmi_env.xa_sw->xa_start_entry(xid, 
0738                                     G_atmi_env.xa_rmid, flags)))
0739     {
0740         if ((flags & TMJOIN || flags & TMRESUME) && XAER_NOTA==ret)
0741         {
0742             need_retry = EXFALSE;
0743         }
0744         else
0745         {
0746             need_retry = EXTRUE;
0747         }
0748         
0749         if (!silent_err || need_retry)
0750         {
0751             NDRX_LOG(log_error, "%s - fail: %d [%s]", 
0752                     __func__, ret, atmi_xa_geterrstr(ret));
0753         }
0754 
0755         /* Core retry engine, no final checks please */
0756         GENERIC_RETRY_CORE(
0757             (G_atmi_env.xa_sw->xa_start_entry(xid, G_atmi_env.xa_rmid, flags))
0758             , (need_retry && is_error_in_recon_list(G_atmi_env.xa_recon_retcodes, ret))
0759             , (XA_OK!=ret)
0760             , EXTRUE);
0761 
0762         if (XA_OK!=ret)
0763         {
0764             if (silent_err && (XAER_NOTA==ret || XAER_DUPID==ret))
0765             {
0766                 /* needs to set error silently.. */
0767                 ndrx_TPset_error_fmt_rsn_silent(TPERMERR,  
0768                         ret, "finally %s - fail: %d [%s]", 
0769                         __func__, ret, atmi_xa_geterrstr(ret));
0770             }
0771             else
0772             {
0773                 NDRX_LOG(log_error, "finally %s - fail: %d [%s]", 
0774                         __func__, ret, atmi_xa_geterrstr(ret));
0775 
0776                 ndrx_TPset_error_fmt_rsn(TPERMERR,  
0777                         ret, "finally %s - fail: %d [%s]", 
0778                         __func__, ret, atmi_xa_geterrstr(ret));
0779             }
0780             goto out;
0781         }
0782     }
0783     
0784 out:
0785     return ret;
0786 }
0787 
0788 
0789 /**
0790  * Disassociate current thread from transaction
0791  * @param xid
0792  * @param flags
0793  * @param[in] aborting if set to EXTRUE, we know that abort will follow.
0794  *  used by posgresql to not to do xa_prepare at the end when rollback will
0795  *  follow
0796  * @return 
0797  */
0798 expublic int atmi_xa_end_entry(XID *xid, long flags, int aborting)
0799 {
0800     int ret = EXSUCCEED;
0801     char stat;
0802     UBFH *p_ub = NULL;
0803     int local_rb = EXFALSE;
0804     GENERIC_RETRY_DEF;
0805     
0806     XA_API_ENTRY(EXTRUE);
0807     
0808     NDRX_LOG(log_debug, "atmi_xa_end_entry flags %ld", flags);
0809     
0810     GENERIC_RETRY_ENTRY(EXFALSE);
0811     
0812     /* we do always success (as TX intiator decides commit or abort...! */
0813     if (XA_OK!=(ret = G_atmi_env.xa_sw->xa_end_entry(xid, 
0814                                     G_atmi_env.xa_rmid, flags)))
0815     {
0816         /* generic retry to keep the connection status with us */
0817         GENERIC_RETRY(
0818             (G_atmi_env.xa_sw->xa_end_entry(xid, G_atmi_env.xa_rmid, flags))
0819             , (is_error_in_recon_list(G_atmi_env.xa_recon_retcodes_other, ret))
0820             , (XA_OK!=ret)
0821             );
0822     }
0823     
0824     /* If having no start xid, then we must call prepare! (for postgresql) */
0825 
0826     if (G_atmi_env.xa_flags_sys & NDRX_XA_FLAG_SYS_NOSTARTXID)
0827     {
0828         NDRX_LOG(log_debug, "NOSTARTXID - preparing at end!");
0829         if (aborting && G_atmi_env.pf_xa_loctxabort)
0830         {
0831             NDRX_LOG(log_info, "Aborting using local rollback func");
0832             local_rb = EXTRUE;
0833             ret = G_atmi_env.pf_xa_loctxabort(xid, TMNOFLAGS);
0834             
0835             if (XA_OK!=ret)
0836             {
0837                 NDRX_LOG(log_error, "Failed to disconnect from transaction: %d", ret);
0838                 userlog("Failed to disconnect from transaction: %d", ret);
0839             }
0840         }
0841         /*
0842         else if (XA_OK!=(ret = G_atmi_env.xa_sw->xa_prepare_entry(xid, 
0843                                         G_atmi_env.xa_rmid, TMNOFLAGS)))
0844          * moved to common prepare engine.
0845          */
0846         else if (XA_OK!=(ret=atmi_xa_prepare_entry(xid, TMNOFLAGS)) && XA_RDONLY!=ret)
0847         {
0848             NDRX_LOG(log_error, "Failed to prepare transaction at NOSTARTXID end");
0849             goto out;
0850         }
0851         
0852         /* test of transaction automatic rollback */
0853         if ((XA_OK==ret || XA_RDONLY == ret) &&
0854                 NDRX_SYSTEST_ENBLD && ndrx_systest_case(NDRX_SYSTEST_ENDPREPFAIL))
0855         {
0856             NDRX_LOG(log_error, "SYSTEST! Generating end-fail error");
0857             atmi_xa_rollback_entry(xid, 0L);
0858             ret = XAER_RMERR;
0859         }
0860         
0861         /* Report status to TMSRV that we performed prepare
0862          * and report the results back...
0863          * If there is failure with TMSRV, then rollback prepared transaction
0864          */
0865         if (local_rb)
0866         {
0867             stat = XA_RM_STATUS_ABORTED;
0868         }
0869         else if (XA_OK==ret)
0870         {
0871             stat = XA_RM_STATUS_PREP;
0872         }
0873         else if (XA_RDONLY==ret)
0874         {
0875             stat = XA_RM_STATUS_COMMITTED_RO;
0876         }
0877         else
0878         {
0879             /* this is rollback */
0880             stat = XA_RM_STATUS_ABORTED;
0881         }
0882         
0883         /* call tmsrv */
0884         
0885         /*
0886          * if call failed due to transaction not found or status unknown
0887          * then we rollback the transaction
0888          */
0889         
0890         NDRX_LOG(log_debug, "Reporting branch transaction status: %c", stat);
0891         p_ub = atmi_xa_call_tm_rmstatus(G_atmi_tls->G_atmi_xa_curtx.txinfo, stat);
0892         
0893         /* if there is matching error, then we abort the  */
0894         if (TPEMATCH==tperrno)
0895         {
0896             NDRX_LOG(log_error, "Got matching error! Abort transaction");
0897             atmi_xa_rollback_entry(xid, 0L);
0898         }
0899         
0900     }
0901     
0902     
0903 out:
0904     if (NULL!=p_ub)
0905     {
0906         tpfree((char *)p_ub);
0907     }
0908                                 
0909     return ret;
0910 }
0911 
0912 /**
0913  * Rollback the transaction.
0914  * @param xid
0915  * @param flags
0916  * @return XA error code
0917  */
0918 expublic int atmi_xa_rollback_entry(XID *xid, long flags)
0919 {
0920     int ret = EXSUCCEED;
0921     GENERIC_RETRY_DEF;
0922     XA_API_ENTRY(EXTRUE);
0923     
0924     NDRX_LOG(log_debug, "atmi_xa_rollback_entry");
0925     
0926     GENERIC_RETRY_ENTRY(EXFALSE);
0927     
0928     if (XA_OK!=(ret = G_atmi_env.xa_sw->xa_rollback_entry(xid, 
0929                                     G_atmi_env.xa_rmid, flags)))
0930     {
0931         /* in case of protocol error, we need to re-open, as connection
0932          * might be is closed.
0933          */
0934         GENERIC_RETRY(
0935             (G_atmi_env.xa_sw->xa_rollback_entry(xid, G_atmi_env.xa_rmid, flags))
0936             , (is_error_in_recon_list(G_atmi_env.xa_recon_retcodes_other, ret))
0937             , (XA_OK!=ret)
0938             );
0939     }
0940     
0941 out:
0942     return ret;
0943 }
0944 
0945 /**
0946  * Prepare for commit current transaction on local resource manager.
0947  * @param xid
0948  * @param flags
0949  * @return XA error code
0950  */
0951 expublic int atmi_xa_prepare_entry(XID *xid, long flags)
0952 {
0953     int ret = EXSUCCEED;
0954     GENERIC_RETRY_DEF;
0955     XA_API_ENTRY(EXTRUE);
0956     
0957     NDRX_LOG(log_debug, "atmi_xa_prepare_entry");
0958     
0959     GENERIC_RETRY_ENTRY(EXFALSE);
0960      
0961     if (XA_OK!=(ret = G_atmi_env.xa_sw->xa_prepare_entry(xid, 
0962                                     G_atmi_env.xa_rmid, flags)))
0963     {
0964         /* no special log needed! */
0965         if (XA_RDONLY==ret)
0966         {
0967             NDRX_LOG(log_debug, "xa_prepare_entry - fail: %d [%s]", 
0968                 ret, atmi_xa_geterrstr(ret));
0969             ndrx_TPset_error_fmt_rsn(TPERMERR,  ret, "xa_prepare_entry - fail: %d [%s]", 
0970                 ret, atmi_xa_geterrstr(ret));
0971         }
0972         else
0973         {
0974             GENERIC_RETRY(
0975                 (G_atmi_env.xa_sw->xa_prepare_entry(xid, G_atmi_env.xa_rmid, flags))
0976                 , (is_error_in_recon_list(G_atmi_env.xa_recon_retcodes_other, ret))
0977                 , (XA_OK!=ret)
0978                  );
0979 
0980         }
0981         
0982         
0983         goto out;
0984     }
0985     
0986 out:
0987     return ret;
0988 }
0989 
0990 /**
0991  * Forget RM known transaction
0992  * @param xid
0993  * @param flags
0994  * @return XA error code
0995  */
0996 expublic int atmi_xa_forget_entry(XID *xid, long flags)
0997 {
0998     int ret = EXSUCCEED;
0999     GENERIC_RETRY_DEF;
1000     XA_API_ENTRY(EXTRUE);
1001     
1002     NDRX_LOG(log_debug, "atmi_xa_forget_entry");
1003      
1004     GENERIC_RETRY_ENTRY(EXFALSE);
1005     
1006     if (XA_OK!=(ret = G_atmi_env.xa_sw->xa_forget_entry(xid, 
1007                                     G_atmi_env.xa_rmid, flags)))
1008     {
1009         /* in case of protocol error, we need to re-open, as connection
1010          * might be is closed.
1011          */
1012         GENERIC_RETRY(
1013                (G_atmi_env.xa_sw->xa_forget_entry(xid, G_atmi_env.xa_rmid, flags))
1014                , (is_error_in_recon_list(G_atmi_env.xa_recon_retcodes_other, ret))
1015                , (XA_OK!=ret)
1016                 );
1017     }
1018     
1019 out:
1020     return ret;
1021 }
1022 
1023 
1024 /**
1025  * Prepare for commit current transaction on local resource manager.
1026  * @param xid
1027  * @param flags
1028  * @return XA error code
1029  */
1030 expublic int atmi_xa_commit_entry(XID *xid, long flags)
1031 {
1032     int ret = EXSUCCEED;
1033     GENERIC_RETRY_DEF;
1034     XA_API_ENTRY(EXTRUE);
1035     
1036     GENERIC_RETRY_ENTRY(EXFALSE);
1037     NDRX_LOG(log_debug, "atmi_xa_commit_entry");
1038     if (XA_OK!=(ret = G_atmi_env.xa_sw->xa_commit_entry(xid, 
1039                                     G_atmi_env.xa_rmid, flags)))
1040     {
1041         GENERIC_RETRY(
1042                 (G_atmi_env.xa_sw->xa_commit_entry(xid,G_atmi_env.xa_rmid, flags))
1043                 , (XAER_RMFAIL==ret)
1044                 , (XA_OK!=ret));
1045     }
1046     
1047 out:
1048     return ret;
1049 }
1050 
1051 /**
1052  * Get the list of transactions associate with RMID
1053  * @param xids xid array
1054  * @param count array len of xids
1055  * @param rmid resource manager id
1056  * @param flags flags
1057  * @return >= number of trans in xid, 
1058  */
1059 expublic int atmi_xa_recover_entry(XID *xids, long count, int rmid, long flags)
1060 {
1061     int ret = EXSUCCEED;
1062     GENERIC_RETRY_DEF;
1063     XA_API_ENTRY(EXTRUE);
1064     
1065     NDRX_LOG(log_debug, "%s", __func__);
1066     
1067     GENERIC_RETRY_ENTRY(EXFALSE);
1068     
1069     if (0 > (ret = G_atmi_env.xa_sw->xa_recover_entry(xids, count,
1070                                     G_atmi_env.xa_rmid, flags)))
1071     {
1072         GENERIC_RETRY(
1073             (G_atmi_env.xa_sw->xa_recover_entry(xids, count, G_atmi_env.xa_rmid, flags))
1074             , (is_error_in_recon_list(G_atmi_env.xa_recon_retcodes_other, ret))
1075             , (0 > ret)
1076             );
1077     }
1078     
1079 out:
1080     return ret;
1081 }
1082 
1083 /******************************************************************************/
1084 /*                          ATMI API                                          */
1085 /******************************************************************************/
1086 
1087 /**
1088  * Begin the global transaction.
1089  * - We should check the context already, maybe we run in transaction already?
1090  * 
1091  * But basically we should call the server, to get the transaction id.
1092  * 
1093  * @param timeout
1094  * @param flags
1095  * @return 
1096  */
1097 expublic int ndrx_tpbegin(unsigned long timeout, long flags)
1098 {
1099     int ret=EXSUCCEED;
1100     UBFH *p_ub = atmi_xa_alloc_tm_call(ATMI_XA_TPBEGIN);
1101     atmi_xa_tx_info_t xai;
1102     long tmflags = 0;
1103     GENERIC_RETRY_DEF;
1104     XA_API_ENTRY(EXTRUE); /* already does ATMI_TLS_ENTRY */
1105     
1106     NDRX_LOG(log_debug, "%s enter", __func__);
1107     
1108     memset(&xai, 0, sizeof(atmi_xa_tx_info_t));
1109     
1110     
1111     if (!G_atmi_tls->G_atmi_xa_curtx.is_xa_open)
1112     {
1113         NDRX_LOG(log_error, "tpbegin: - tpopen() was not called!");
1114         ndrx_TPset_error_msg(TPEPROTO,  "tpbegin - tpopen() was not called!");
1115         EXFAIL_OUT(ret);
1116     }
1117 
1118     if (0!=flags)
1119     {
1120         NDRX_LOG(log_error, "tpbegin: flags != 0");
1121         ndrx_TPset_error_msg(TPEINVAL,  "tpbegin: flags != 0");
1122         EXFAIL_OUT(ret);
1123     }
1124     
1125     /* If we have active transaction, then we are in txn mode.. */
1126     if (G_atmi_tls->G_atmi_xa_curtx.txinfo)
1127     {
1128         NDRX_LOG(log_error, "tpbegin: - already in transaction mode XID: [%s]", 
1129                 G_atmi_tls->G_atmi_xa_curtx.txinfo->tmxid);
1130         ndrx_TPset_error_fmt(TPEPROTO,  "tpbegin: - already in transaction mode XID: [%s]", 
1131                 G_atmi_tls->G_atmi_xa_curtx.txinfo->tmxid);
1132         EXFAIL_OUT(ret);
1133     }
1134     
1135     NDRX_LOG(log_debug, "About to call TM");
1136     /* Load the timeout param to FB... */
1137     if (EXSUCCEED!=Bchg(p_ub, TMTXTOUT, 0, (char *)&timeout, 0L))
1138     {
1139         ndrx_TPset_error_fmt(TPESYSTEM,  "tpbegin: - failed to fill FB - set TMTXTOUT!");
1140         EXFAIL_OUT(ret);
1141     }
1142     
1143     if (XA_IS_DYNAMIC_REG)
1144     {
1145         /* tell RM, then we use dynamic reg (so that it does not register 
1146          * local RMs work)
1147          */
1148         tmflags|=TMFLAGS_DYNAMIC_REG;
1149     }
1150     
1151     if (G_atmi_env.xa_flags_sys & NDRX_XA_FLAG_SYS_NOSTARTXID)
1152     {
1153         tmflags|=TMFLAGS_TPNOSTARTXID;
1154     }
1155     
1156     if (EXSUCCEED!=Bchg(p_ub, TMTXFLAGS, 0, (char *)&tmflags, 0L))
1157     {
1158         ndrx_TPset_error_fmt(TPESYSTEM,  "tpbegin: - failed to fill FB - set TMTXFLAGS!");
1159         EXFAIL_OUT(ret);
1160     }
1161     
1162     /* OK, we should call the server, request for transaction...  */
1163     if (NULL==(p_ub=atmi_xa_call_tm_generic_fb(ATMI_XA_TPBEGIN, NULL, EXTRUE, EXFAIL, 
1164             NULL, p_ub)))
1165     {
1166         NDRX_LOG(log_error, "Failed to execute TM command [%c]", 
1167                     ATMI_XA_TPBEGIN);
1168         /* _TPoverride_code(TPETRAN);  - WHY?*/
1169         EXFAIL_OUT(ret);
1170     }
1171     /* We should load current context with transaction info we got 
1172      * + we should join the transaction i.e. current thread.
1173      */
1174     
1175     if (EXSUCCEED!=atmi_xa_read_tx_info(p_ub, &xai, 0))
1176     {
1177         NDRX_LOG(log_error, "tpbegin: - failed to read TM response");
1178         ndrx_TPset_error_msg(TPEPROTO,  "tpbegin: - failed to read TM response");
1179         EXFAIL_OUT(ret);
1180     }
1181     
1182     NDRX_LOG(log_debug, "About to load tx info");
1183     
1184     /* Only when we have in transaction, then install the handler */
1185     if (EXSUCCEED!= atmi_xa_set_curtx_from_xai(&xai))
1186     {
1187         NDRX_LOG(log_error, "tpbegin: - failed to set curren tx");
1188         ndrx_TPset_error_msg(TPEPROTO,  "tpbegin: - failed to set curren tx");
1189         EXFAIL_OUT(ret);
1190     }
1191     /*G_atmi_xa_curtx.is_in_tx = TRUE;*/
1192     G_atmi_tls->G_atmi_xa_curtx.txinfo->tranid_flags |= XA_TXINFO_INITIATOR;
1193     
1194     /* OK... now join the transaction (if we are static...) (only if static) */
1195     if (!XA_IS_DYNAMIC_REG)
1196     {
1197         if (EXSUCCEED!=atmi_xa_start_entry(atmi_xa_get_branch_xid(&xai, xai.btid), 
1198                 TMNOFLAGS, EXFALSE))
1199         {
1200             /* got to rollback the curren transaction*/
1201             NDRX_LOG(log_error, "Failed to join transaction!");
1202             ndrx_xa_join_fail(NULL, EXFALSE);
1203             atmi_xa_reset_curtx();
1204             EXFAIL_OUT(ret);
1205         }
1206         
1207         /* Already set by TM
1208         atmi_xa_curtx_set_cur_rmid(&xai);
1209          */
1210     }
1211     else
1212     {
1213         NDRX_LOG(log_debug, "Working in dynamic mode...");
1214         /* if connection is bad, please reconect */
1215         GENERIC_RETRY_ENTRY(EXTRUE);
1216     }
1217 
1218     NDRX_LOG(log_debug, "Process joined to transaction [%s] OK",
1219                         G_atmi_tls->G_atmi_xa_curtx.txinfo->tmxid);
1220     
1221 out:
1222 
1223     /* TODO: We need remove curren transaction from HASH! */
1224     if (NULL!=p_ub)
1225     {
1226         /* save errors */
1227         atmi_error_t err;
1228         
1229         /* Save the original error/needed later! */
1230         ndrx_TPsave_error(&err);
1231         tpfree((char *)p_ub);  /* This stuff removes ATMI error!!! */
1232         ndrx_TPrestore_error(&err);
1233     }
1234     return ret;
1235 }
1236 
1237 
1238 /**
1239  * Set when to return from commit. Either when transaction is completed
1240  * or when transaction is logged in persisted device for further background
1241  * completion.
1242  * The default is TP_CMT_COMPLETE.
1243  * @param flags TP_CMT_LOGGED or TP_CMT_COMPLETE
1244  * @return EXSUCCEED/EXFAIL
1245  */
1246 expublic int ndrx_tpscmt(long flags)
1247 {
1248     int ret = EXSUCCEED;
1249     
1250     if (TP_CMT_LOGGED!=flags &&
1251             TP_CMT_COMPLETE!=flags)
1252     {
1253         NDRX_LOG(log_error, "Invalid value: commit return %ld", (long)flags);
1254         ndrx_TPset_error_fmt(TPEINVAL,  "Invalid value: commit return %ld", 
1255                 (long)flags);
1256         EXFAIL_OUT(ret);
1257     }
1258     
1259     if (TX_COMMIT_COMPLETED==G_atmi_tls->tx_commit_return)
1260     {
1261         ret = TP_CMT_COMPLETE;
1262     }
1263     else
1264     {
1265         ret = TP_CMT_LOGGED;
1266     }
1267     
1268     if (TP_CMT_COMPLETE==flags)
1269     {
1270         G_atmi_tls->tx_commit_return = TX_COMMIT_COMPLETED;
1271     }
1272     
1273     if (TP_CMT_LOGGED==flags)
1274     {
1275         G_atmi_tls->tx_commit_return = TX_COMMIT_DECISION_LOGGED;
1276     }
1277     
1278     NDRX_LOG(log_info, "Commit return set to %ld (TX) ret %d", 
1279             (long)G_atmi_tls->tx_commit_return, ret);
1280     
1281 out:
1282     return ret;
1283 }
1284 
1285 /**
1286  * API implementation of tpcommit
1287  *
1288  * @param timeout
1289  * @param flags TPTXCOMMITDLOG - return when prepared
1290  * @return EXSUCCEED/EXFAIL
1291  */
1292 expublic int ndrx_tpcommit(long flags)
1293 {
1294     int ret=EXSUCCEED;
1295     UBFH *p_ub = NULL;
1296     int do_abort = EXFALSE;
1297     XA_API_ENTRY(EXTRUE); /* already does ATMI_TLS_ENTRY; */
1298     
1299     NDRX_LOG(log_debug, "%s enter", __func__);
1300     
1301     if (!G_atmi_tls->G_atmi_xa_curtx.is_xa_open)
1302     {
1303         NDRX_LOG(log_error, "tpcommit: - tpopen() was not called!");
1304         ndrx_TPset_error_msg(TPEPROTO,  "tpcommit - tpopen() was not called!");
1305         ret=EXFAIL;
1306         goto out_no_reset;
1307     }
1308 
1309     if (0!=flags && !(flags & TPTXCOMMITDLOG))
1310     {
1311         NDRX_LOG(log_error, "tpcommit: flags != 0 && !TPTXCOMMITDLOG");
1312         ndrx_TPset_error_msg(TPEINVAL,  "tpcommit: flags != 0 && !TPTXCOMMITDLOG");
1313         ret=EXFAIL;
1314         goto out_no_reset;
1315     }
1316     
1317     if (!G_atmi_tls->G_atmi_xa_curtx.txinfo)
1318     {
1319         NDRX_LOG(log_error, "tpcommit: Not in global TX");
1320         ndrx_TPset_error_msg(TPEPROTO,  "tpcommit: Not in global TX");
1321         ret=EXFAIL;
1322         goto out_no_reset;
1323         
1324     }
1325             
1326     /* allow commit even, if we are not the initiators,
1327      * but for auto-tran this is OK
1328      */
1329     if (!G_atmi_tls->G_atmi_xa_curtx.txinfo->tranid_flags)
1330     {
1331         NDRX_LOG(log_error, "tpcommit: Not not initiator");
1332         ndrx_TPset_error_msg(TPEPROTO,  "tpcommit: Not not initiator");
1333         ret=EXFAIL;
1334         goto out_no_reset;
1335     }
1336     
1337     /* flag is shared with tx: */
1338     if (TX_COMMIT_DECISION_LOGGED == G_atmi_tls->tx_commit_return)
1339     {
1340         flags|=TPTXCOMMITDLOG;
1341     }
1342     
1343     /* Check situation with call descriptors */
1344     if (atmi_xa_cd_isanyreg(&(G_atmi_tls->G_atmi_xa_curtx.txinfo->call_cds)))
1345     {
1346         NDRX_LOG(log_error, "tpcommit: Open call descriptors found - abort!");
1347         do_abort = EXTRUE;
1348     }
1349     
1350     if (atmi_xa_cd_isanyreg(&(G_atmi_tls->G_atmi_xa_curtx.txinfo->conv_cds)))
1351     {
1352         NDRX_LOG(log_error, "tpcommit: Open conversation descriptors found - abort!");
1353         do_abort = EXTRUE;
1354     }
1355     
1356     if (G_atmi_tls->G_atmi_xa_curtx.txinfo->tmtxflags & TMTXFLAGS_IS_ABORT_ONLY)
1357     {
1358         NDRX_LOG(log_error, "tpcommit: Transaction marked as abort only!");
1359         do_abort = EXTRUE;
1360     }
1361     
1362     /* Disassoc from transaction! */
1363     
1364     /* TODO: Detect when we need the END entry. 
1365      * it should be work_done, or static reg!!!
1366      */
1367     if (!XA_IS_DYNAMIC_REG || 
1368             (XA_TXINFO_AXREG_CLD & G_atmi_tls->G_atmi_xa_curtx.txinfo->tranid_flags))
1369     {
1370         if (EXSUCCEED!= (ret=atmi_xa_end_entry(atmi_xa_get_branch_xid(G_atmi_tls->G_atmi_xa_curtx.txinfo, 
1371                 G_atmi_tls->G_atmi_xa_curtx.txinfo->btid), TMSUCCESS, EXFALSE)))
1372         {
1373             NDRX_LOG(log_error, "Failed to end XA api: %d [%s] - aborting", 
1374                     ret, atmi_xa_geterrstr(ret));
1375             userlog("Failed to end XA api: %d [%s] - aborting", 
1376                     ret, atmi_xa_geterrstr(ret));
1377             
1378             do_abort = EXTRUE;
1379         }
1380     }
1381     
1382     if (do_abort)
1383     {
1384         /* common termination at commit. */
1385         ret = ndrx_tpabort(0, EXFALSE);
1386         
1387         /* in this case the tmsrv might already rolled back
1388          * thus assume that transaction is aborted.
1389          */
1390         if (EXSUCCEED==ret || TPEPROTO==tperrno)
1391         {
1392             /* clear current error */
1393             ndrx_TPunset_error();
1394             ndrx_TPset_error_msg(TPEABORT,  "tpcommit: Transaction was marked for "
1395                     "abort and aborted now!");
1396             ret=EXFAIL;
1397         }
1398         
1399         return ret; /*<<<<<<<<<< RETURN!!! */
1400     }
1401     
1402     NDRX_LOG(log_debug, "About to call TM flags=%ld", flags);
1403     /* OK, we should call the server, request for transaction...  */
1404     
1405     /* TODO: pass flags to call struct! */
1406     if (NULL==(p_ub=atmi_xa_call_tm_generic(ATMI_XA_TPCOMMIT, EXFALSE, EXFAIL, 
1407             G_atmi_tls->G_atmi_xa_curtx.txinfo, flags, EXFAIL)))
1408     {
1409         NDRX_LOG(log_error, "Failed to execute TM command [%c]", 
1410                     ATMI_XA_TPCOMMIT);
1411         
1412         /* _TPoverride_code(TPETRAN); */
1413         
1414         EXFAIL_OUT(ret);
1415     }
1416 
1417     NDRX_LOG(log_debug, "Transaction [%s] commit OK",
1418                         G_atmi_tls->G_atmi_xa_curtx.txinfo->tmxid);
1419         
1420 out:
1421                             
1422     /* reset global transaction info */
1423     atmi_xa_reset_curtx();
1424 
1425 out_no_reset:
1426 
1427     if (NULL!=p_ub)
1428     {
1429         /* save errors */
1430         atmi_error_t err;
1431         
1432         /* Save the original error/needed later! */
1433         ndrx_TPsave_error(&err);
1434         tpfree((char *)p_ub);  /* This stuff removes ATMI error!!! */
1435         ndrx_TPrestore_error(&err);
1436     }
1437 
1438 
1439     return ret;
1440 }
1441 
1442 
1443 /**
1444  * API implementation of tpabort
1445  * @param timeout
1446  * @param flags
1447  * @param call_xa_end shall the xa_end() be called?
1448  * @return 
1449  */
1450 expublic int ndrx_tpabort(long flags, int call_xa_end)
1451 {
1452     int ret=EXSUCCEED;
1453     UBFH *p_ub = NULL;
1454     XA_API_ENTRY(EXTRUE); /* already does ATMI_TLS_ENTRY; */
1455     
1456     NDRX_LOG(log_debug, "_tpabort enter");
1457     
1458     if (!G_atmi_tls->G_atmi_xa_curtx.is_xa_open)
1459     {
1460         NDRX_LOG(log_error, "tpabort: - tpopen() was not called!");
1461         ndrx_TPset_error_msg(TPEPROTO,  "tpabort - tpopen() was not called!");
1462         ret=EXFAIL;
1463         goto out_no_reset;
1464     }
1465 
1466     if (0!=flags)
1467     {
1468         NDRX_LOG(log_error, "tpabort: flags != 0");
1469         ndrx_TPset_error_msg(TPEINVAL,  "tpabort: flags != 0");
1470         ret=EXFAIL;
1471         goto out_no_reset;
1472     }
1473     
1474     if (!G_atmi_tls->G_atmi_xa_curtx.txinfo)
1475     {
1476         NDRX_LOG(log_error, "tpabort: Not in global TX");
1477         ndrx_TPset_error_msg(TPEPROTO,  "tpabort: Not in global TX");
1478         ret=EXFAIL;
1479         goto out_no_reset;
1480     }
1481             
1482     if (!(XA_TXINFO_INITIATOR & G_atmi_tls->G_atmi_xa_curtx.txinfo->tranid_flags))
1483     {
1484         NDRX_LOG(log_error, "tpabort: Not not initiator");
1485         ndrx_TPset_error_msg(TPEPROTO,  "tpabort: Not not initiator");
1486         ret=EXFAIL;
1487         goto out_no_reset;
1488     }
1489     
1490     /* Disassoc from transaction! */
1491     if (call_xa_end)
1492     {
1493         if (!XA_IS_DYNAMIC_REG || 
1494                 (XA_TXINFO_AXREG_CLD & G_atmi_tls->G_atmi_xa_curtx.txinfo->tranid_flags))
1495         {
1496             /* abort anyway... */
1497             if (EXSUCCEED!= atmi_xa_end_entry(
1498                     atmi_xa_get_branch_xid(G_atmi_tls->G_atmi_xa_curtx.txinfo,
1499                     G_atmi_tls->G_atmi_xa_curtx.txinfo->btid), TMSUCCESS, EXTRUE))
1500             {
1501                 NDRX_LOG(log_error, "Failed to end XA api: %d [%s]", 
1502                         ret, atmi_xa_geterrstr(ret));
1503                 userlog("Failed to end XA api: %d [%s]", 
1504                         ret, atmi_xa_geterrstr(ret));
1505             }
1506         }
1507     }
1508     
1509     NDRX_LOG(log_debug, "About to call TM");
1510     /* OK, we should call the server, request for transaction...  */
1511     if (NULL==(p_ub=atmi_xa_call_tm_generic(ATMI_XA_TPABORT, EXFALSE, EXFAIL, 
1512             G_atmi_tls->G_atmi_xa_curtx.txinfo, 0L, EXFAIL)))
1513     {
1514         NDRX_LOG(log_error, "Failed to execute TM command [%c]", 
1515                     ATMI_XA_TPBEGIN);
1516         
1517         /* _TPoverride_code(TPETRAN); */
1518         
1519         EXFAIL_OUT(ret);
1520     }
1521 
1522     NDRX_LOG(log_debug, "Transaction [%s] abort OK",
1523                         G_atmi_tls->G_atmi_xa_curtx.txinfo->tmxid);
1524 out:
1525     /* reset global transaction info */
1526     atmi_xa_reset_curtx();
1527 
1528 out_no_reset:
1529 
1530     if (NULL!=p_ub)
1531     {
1532         /* save errors */
1533         atmi_error_t err;
1534         
1535         /* Save the original error/needed later! */
1536         ndrx_TPsave_error(&err);
1537         tpfree((char *)p_ub);  /* This stuff removes ATMI error!!! */
1538         ndrx_TPrestore_error(&err);
1539     }
1540 
1541     return ret;
1542 }
1543 
1544 /**
1545  * Open the entry to XA.
1546  * @return 
1547  */
1548 expublic int ndrx_tpopen(void)
1549 {
1550     int ret=EXSUCCEED;
1551     XA_API_ENTRY(EXTRUE);
1552    
1553     ret = atmi_xa_open_entry();
1554     
1555 out:
1556     return ret;
1557 }
1558 
1559 /**
1560  * Close the entry to XA.
1561  * @return EXSUCCEED/EXFAIL
1562  */
1563 expublic int ndrx_tpclose(void)
1564 {
1565     int ret=EXSUCCEED;
1566     
1567     XA_API_ENTRY(EXTRUE);
1568 
1569     if (G_atmi_tls->G_atmi_xa_curtx.txinfo)
1570     {
1571         NDRX_LOG(log_error, "tpclose: - cannot close as process in TX: [%s]", 
1572                 G_atmi_tls->G_atmi_xa_curtx.txinfo->tmxid);
1573         ndrx_TPset_error_fmt(TPEPROTO, "tpclose: - cannot close as process in TX: [%s]", 
1574                 G_atmi_tls->G_atmi_xa_curtx.txinfo->tmxid);
1575         EXFAIL_OUT(ret);
1576     }
1577    
1578     ret = atmi_xa_close_entry(EXFALSE);
1579     
1580 out:
1581     return ret;
1582 }
1583  
1584 /**
1585  * Suspend the current transaction in progress.
1586  * Note we do not care is it server or client. Global transaction (even participants)
1587  * can be moved to another external process.
1588  * 
1589  * NODE: we might get additional error code:TPERMERR when xa_end fails.
1590  * @param tranid
1591  * @param flags
1592  * @param[in] is_contexting are we doing context swtching? 
1593  *  and if driver flags sys that no api suspend needed, then do not call the xa-commands
1594  *  particulary this is needed for java api for which transaction state is kept in
1595  *  java object and not in C TLS.
1596  * @return SUCCEED/FAIL
1597  */
1598 expublic int ndrx_tpsuspend (TPTRANID *tranid, long flags, int is_contexting)
1599 {
1600     int ret=EXSUCCEED;
1601     long xa_flags = TMSUCCESS;
1602     
1603     XA_API_ENTRY(EXTRUE); /* already does ATMI_TLS_ENTRY; */
1604     NDRX_LOG(log_info, "Suspending global transaction: atmi flags %lx", flags);
1605     if (NULL==tranid)
1606     {
1607         ndrx_TPset_error_msg(TPEINVAL,  "_tpsuspend: trandid = NULL!");
1608         EXFAIL_OUT(ret);
1609     }
1610     
1611     if (0!= (flags & ~TPTXTMSUSPEND) )
1612     {
1613         ndrx_TPset_error_msg(TPEINVAL,  "_tpsuspend: flags is not 0, nor TPTXTMSUSPEND");
1614         EXFAIL_OUT(ret);
1615     }
1616     
1617     if (!G_atmi_tls->G_atmi_xa_curtx.txinfo)
1618     {
1619         NDRX_LOG(log_error, "_tpsuspend: Not in global TX");
1620         ndrx_TPset_error_msg(TPEPROTO,  "_tpsuspend: Not in global TX");
1621         EXFAIL_OUT(ret);
1622     }
1623     
1624     /* 
1625      * for no-join we will continue as is with TMSUCCESS, as TMSUSPEND
1626      * is not supported 100%
1627      */
1628     if ( (flags & TPTXTMSUSPEND) && !(G_atmi_env.xa_flags_sys & NDRX_XA_FLAG_SYS_NOJOIN))
1629     {
1630         xa_flags = TMSUSPEND;
1631     }
1632     
1633 #if 0
1634     - I guess this is not a problem. Must be able to suspend abort only transaction
1635     because of object-api
1636     if (G_atmi_tls->G_atmi_xa_curtx.txinfo->tmtxflags & TMTXFLAGS_IS_ABORT_ONLY)
1637     {
1638         NDRX_LOG(log_error, "_tpsuspend: Abort only transaction!");
1639         ndrx_TPset_error_msg(TPEPROTO,  "_tpsuspend: Abort only transaction!");
1640         EXFAIL_OUT(ret);
1641     }
1642 #endif
1643     
1644     /* Check situation with call descriptors */
1645     if (!is_contexting  /* do not check call descriptors in contexting mode */
1646             && atmi_xa_cd_isanyreg(&(G_atmi_tls->G_atmi_xa_curtx.txinfo->call_cds)))
1647     {
1648         NDRX_LOG(log_error, "_tpsuspend: Call descriptors still open!");
1649         ndrx_TPset_error_msg(TPEPROTO,  "_tpsuspend: Call descriptors still open!");
1650         EXFAIL_OUT(ret);
1651     }
1652     
1653     if (!is_contexting /* do not check call descriptors in contexting mode */
1654             && atmi_xa_cd_isanyreg(&(G_atmi_tls->G_atmi_xa_curtx.txinfo->conv_cds)))
1655     {
1656         NDRX_LOG(log_error, "_tpsuspend: Conversation descriptors still open!");
1657         ndrx_TPset_error_msg(TPEPROTO,  "_tpsuspend: Conversation descriptors still open!");
1658         EXFAIL_OUT(ret);
1659     }
1660     
1661     /* Now transfer current transaction data from one struct to another... */
1662     
1663     XA_TX_COPY(tranid, G_atmi_tls->G_atmi_xa_curtx.txinfo);
1664     tranid->is_tx_initiator = G_atmi_tls->G_atmi_xa_curtx.txinfo->tranid_flags;
1665     
1666     /* TODO: if join is not supported, then we terminate current transaction/BTID
1667      * and that shall be removed from list.
1668      * That is done by atmi_xa_reset_curtx.
1669      * Thus at this point we just end our journey wit this BTID
1670      */
1671     
1672     /* Disassoc from transaction! */
1673     if (!XA_IS_DYNAMIC_REG || 
1674             (XA_TXINFO_AXREG_CLD & G_atmi_tls->G_atmi_xa_curtx.txinfo->tranid_flags))
1675     {
1676         /*
1677      * causes ORA-24775 error
1678         long xaflags = TMSUSPEND;
1679         
1680         if (!(G_atmi_env.xa_sw->flags & TMNOMIGRATE))
1681         {
1682             NDRX_LOG(log_debug, "Setting migrate flag for suspend");
1683             xaflags|=TMMIGRATE;
1684         }
1685         */
1686         
1687         if (EXSUCCEED!= (ret=atmi_xa_end_entry(
1688                 atmi_xa_get_branch_xid(G_atmi_tls->G_atmi_xa_curtx.txinfo,
1689                 G_atmi_tls->G_atmi_xa_curtx.txinfo->btid), xa_flags, EXFALSE)))
1690         {
1691             int did_abort = EXFALSE;
1692             NDRX_LOG(log_error, "Failed to end XA api: %d [%s] flags: %lx", 
1693                     ret, atmi_xa_geterrstr(ret), xa_flags);
1694             userlog("Failed to end XA api: %d [%s] flags: %lx", 
1695                     ret, atmi_xa_geterrstr(ret), xa_flags);
1696             
1697             
1698             ndrx_xa_join_fail(&did_abort, EXFALSE);
1699             
1700             if (did_abort)
1701             {
1702                 ndrx_TPoverride_code(TPEABORT);
1703             }
1704             else
1705             {
1706                 ndrx_TPoverride_code(TPESYSTEM);
1707             }
1708             
1709             goto out;
1710         }
1711     }
1712 
1713     atmi_xa_reset_curtx();
1714     
1715     NDRX_LOG(log_debug, "Suspend ok xid: [%s] xa flags: %lx", 
1716             tranid->tmxid, xa_flags);
1717 out:
1718 
1719     return ret;
1720 }
1721 
1722 /**
1723  * Resume suspended transaction
1724  * @param tranid transaction id object
1725  * @param flags TPTXNOOPTIM - do not use optimization known RMs
1726  * @return EXUSCCEED/EXFAIL
1727  */
1728 expublic int  ndrx_tpresume (TPTRANID *tranid, long flags)
1729 {
1730     int ret=EXSUCCEED;
1731     int was_join = EXFALSE;
1732     long join_flag = TMJOIN;
1733     atmi_xa_tx_info_t xai;
1734     
1735     XA_API_ENTRY(EXTRUE); /* already does ATMI_TLS_ETNRY; */
1736     NDRX_LOG(log_info, "Resuming global transaction...");
1737     
1738     if (NULL==tranid)
1739     {
1740         ndrx_TPset_error_msg(TPEINVAL,  "_tpresume: trandid = NULL!");
1741         EXFAIL_OUT(ret);
1742     }
1743        
1744     if (0!= (flags & ~ (TPTXNOOPTIM | TPTXTMSUSPEND)) )
1745     {
1746         ndrx_TPset_error_msg(TPEINVAL,  "_tpresume: flags is not 0, "
1747                 "nor TPTXNOOPTIM, nor TPTXTMSUSPEND");
1748         EXFAIL_OUT(ret);
1749     }
1750     
1751     /* Resume the trany 
1752      * Also if operating in nojoin mode, then there is no need for this flag.
1753      * Thought all could be handled in _tp_srv_join_or_new
1754      * but anyway for consistency will leave this peace of code here.
1755      */
1756     if ( (flags & TPTXTMSUSPEND)
1757             && !(G_atmi_env.xa_flags_sys & NDRX_XA_FLAG_SYS_NOJOIN))
1758     {
1759         join_flag = TMRESUME;
1760     }
1761     
1762     /* NOTE: TPEMATCH - not tracked. */
1763     if (G_atmi_tls->G_atmi_xa_curtx.txinfo)
1764     {
1765         ndrx_TPset_error_msg(TPEPROTO,  "_tpresume: Already in global TX!");
1766         EXFAIL_OUT(ret);
1767     }
1768     
1769     /* Copy off the tx info to call */
1770     XA_TX_COPY((&xai), tranid);
1771     
1772     /* do not use optimization data... */
1773     if (flags & TPTXNOOPTIM)
1774     {
1775         xai.tmknownrms[0]=EXEOS;
1776     }
1777     
1778     if (EXSUCCEED!=_tp_srv_join_or_new(&xai, EXFALSE, &was_join, join_flag,
1779             tranid->is_tx_initiator))
1780     {
1781         ndrx_TPset_error_msg(TPESYSTEM,  "_tpresume: Failed to enter in global TX!");
1782         EXFAIL_OUT(ret);
1783     }
1784     
1785     NDRX_LOG(log_debug, "Resume ok xid: [%s] is_tx_initiator: %d abort_only: %d", 
1786             tranid->tmxid, tranid->is_tx_initiator, 
1787             G_atmi_tls->G_atmi_xa_curtx.txinfo->tmtxflags & TMTXFLAGS_IS_ABORT_ONLY);
1788     
1789 out:
1790     return ret;
1791 }
1792 
1793 /**
1794  * Database is registering transaction in progress...
1795  * @param rmid
1796  * @param xid
1797  * @param flags
1798  * @return 
1799  */
1800 expublic int ax_reg(int rmid, XID *xid, long flags)
1801 {
1802     int ret = TM_OK;
1803     int was_join = EXFALSE;
1804     ATMI_TLS_ENTRY;
1805     
1806     NDRX_LOG(log_info, "ax_reg called");
1807     if (NULL==G_atmi_tls->G_atmi_xa_curtx.txinfo)
1808     {
1809         NDRX_LOG(log_error, "ERROR: No global transaction registered "
1810                 "with process/thread!");
1811         userlog("ERROR: No global transaction registered with process/thread!");
1812         memset(xid, 0, sizeof(XID));
1813         ret = TMER_TMERR;
1814         goto out;
1815     }
1816     
1817     if (EXSUCCEED!=_tp_srv_join_or_new(G_atmi_tls->G_atmi_xa_curtx.txinfo, 
1818             EXTRUE, &was_join, TMJOIN, G_atmi_tls->G_atmi_xa_curtx.txinfo->tranid_flags))
1819     {
1820         ret = TMER_TMERR;
1821         goto out;
1822     }
1823     
1824     if (was_join)
1825     {
1826         ret = TM_JOIN;
1827     }
1828     
1829     memcpy(xid, atmi_xa_get_branch_xid(G_atmi_tls->G_atmi_xa_curtx.txinfo, 
1830             G_atmi_tls->G_atmi_xa_curtx.txinfo->btid), sizeof(*xid));
1831     
1832     /* why? already handled by _tp_srv_join_or_new()
1833     G_atmi_tls->G_atmi_xa_curtx.txinfo->tranid_flags |= XA_TXINFO_AXREG_CLD;
1834     */
1835   
1836 out:
1837     NDRX_LOG(log_info, "ax_reg returns: %d", ret);
1838     return ret;
1839 }
1840  
1841 /**
1842  * DB is un-registering the transaction
1843  * @param rmid
1844  * @param flags
1845  * @return 
1846  */
1847 int ax_unreg(int rmid, long flags)
1848 {
1849     NDRX_LOG(log_info, "ax_unreg called");
1850     return EXSUCCEED;
1851 }
1852 
1853 /**
1854  * Wrapper with call structure 
1855  * @param call
1856  * @return 
1857  */
1858 expublic int _tp_srv_join_or_new_from_call(tp_command_call_t *call,
1859         int is_ax_reg_callback)
1860 {
1861     int is_known = EXFALSE;
1862     atmi_xa_tx_info_t xai;
1863     memset(&xai, 0, sizeof(xai));
1864     /* get the xai struct */
1865     /*
1866     atmi_xa_xai_from_call(&xai, call);
1867      */
1868     XA_TX_COPY((&xai), call)
1869     
1870     return _tp_srv_join_or_new(&xai, is_ax_reg_callback, &is_known, TMJOIN, 
1871             XA_TXINFO_NOFLAGS);
1872 }
1873 
1874 /**
1875  * In case of for any process join have failed we shall report this
1876  * to TMSRV so that it can rollback the transaction as soon as possible.
1877  * Otherwise the caller might have lost the control of the transaction
1878  * and thus it would not be able to rollback it, and thus it will wait
1879  * timeout at tmsrv (which could be long) so better notify tmsrv to
1880  * perform abort. This assume that current transaction is set.
1881  * @param did_abort is transaction aborted at the end?
1882  * @param force_abort shall abort happen?
1883  * @return EXSUCCED/EXFAIL
1884  */
1885 exprivate int ndrx_xa_join_fail(int *did_abort, int force_abort)
1886 {
1887     UBFH *p_ub = NULL;
1888     int ret = EXSUCCEED;
1889     /* save errors */
1890     atmi_error_t err;
1891     
1892     /* no action, as we did not start the transaction
1893      * so that client can finalize
1894      */
1895     if (!(G_atmi_tls->G_atmi_xa_curtx.txinfo->tranid_flags & XA_TXINFO_INITIATOR) &&
1896             !force_abort)
1897     {
1898         return EXSUCCEED;
1899     }
1900         
1901     /* Save the original error/needed later! */
1902     ndrx_TPsave_error(&err);
1903         
1904     NDRX_LOG(log_error, "xa_start() or xa_end() failed, aborting to TMSRV");
1905     
1906     if (NULL==(p_ub=atmi_xa_call_tm_generic(ATMI_XA_TPABORT, EXFALSE, EXFAIL, 
1907             G_atmi_tls->G_atmi_xa_curtx.txinfo, 0L, EXFAIL)))
1908     {
1909         NDRX_LOG(log_error, "Failed to execute TM command [%c]", 
1910                     ATMI_XA_TPABORT);
1911         
1912         /* _TPoverride_code(TPETRAN); */
1913         
1914         EXFAIL_OUT(ret);
1915     }
1916     
1917     if (NULL!=did_abort)
1918     {
1919         *did_abort = EXTRUE;
1920     }
1921     
1922 out:
1923     if (NULL!=p_ub)
1924     {
1925         tpfree((char *)p_ub);  /* This stuff removes ATMI error!!! */
1926         
1927     }
1928     ndrx_TPrestore_error(&err);
1929     
1930     return ret;
1931 }
1932 
1933 /**
1934  * Process should try to join the XA, if fails, then create new transaction
1935  * @param call
1936  * @param join_flag override the default join setting (for TMRESUME)
1937  *  default for callers shall be TMJOIN
1938  * @param tranid_flags local / tranid flags of the transaction
1939  * @return SUCCEED/FAIL
1940  */
1941 expublic int _tp_srv_join_or_new(atmi_xa_tx_info_t *p_xai,
1942         int is_ax_reg_callback, int *p_is_known, long join_flag, int tranid_flags)
1943 {
1944     int ret = EXSUCCEED;
1945     UBFH *p_ub = NULL;
1946     short reason;
1947     int new_rm = EXFALSE;
1948     char src_tmknownrms[2];
1949     long tmflags = 0;
1950     GENERIC_RETRY_DEF;
1951 
1952     XA_API_ENTRY(EXTRUE); /* already does ATMI_TLS_ENTRY; */
1953     
1954     /* Do the same static flow if ax_reg was already called in dynamic mode
1955      * this might be true if coming back from tpsuspend()
1956      * thus we shall perform xa_start.
1957      */
1958     if (!(XA_IS_DYNAMIC_REG) || (tranid_flags & XA_TXINFO_AXREG_CLD))
1959     {
1960         
1961         if (EXSUCCEED!=atmi_xa_set_curtx_from_xai(p_xai))
1962         {
1963             EXFAIL_OUT(ret);
1964         }
1965         
1966         /* keep the origin flag. */
1967         G_atmi_tls->G_atmi_xa_curtx.txinfo->tranid_flags = tranid_flags;
1968     }
1969     else
1970     {
1971         /* 
1972          * this is Dynamic registration.
1973          * 
1974          * This is first time server joins, no work yet done.
1975          */
1976         if (!is_ax_reg_callback)
1977         {
1978             NDRX_LOG(log_debug, "Dynamic reg + process start "
1979                                 "just remember the transaction");
1980             
1981             /* if connection is bad, please reconect */
1982             GENERIC_RETRY_ENTRY(EXTRUE);
1983 
1984             /* OK, but how BTID is filled?,
1985              * BTID is set on second pass by bellow common source
1986              */
1987             if (EXSUCCEED!=atmi_xa_set_curtx_from_xai(p_xai))
1988             {
1989                 EXFAIL_OUT(ret);
1990             }
1991 
1992             /* keep the origin flag. */
1993             G_atmi_tls->G_atmi_xa_curtx.txinfo->tranid_flags = tranid_flags;
1994 
1995             /* if connection is bad, please reconect */
1996             GENERIC_RETRY_ENTRY(EXTRUE);
1997 
1998             /* Do not do anything more... */
1999             goto out;
2000         }
2001         else
2002         {
2003             /*
2004              * At this point resource is modified. Note that if we perform
2005              * resume on such transaction, we shall do the xa_start()
2006              * mark current thread as involved (needs xa_end()!) 
2007              * actual join to the transaction
2008              */
2009             NDRX_LOG(log_debug, "Dynamic reg work started");
2010             p_xai->tranid_flags|=XA_TXINFO_AXREG_CLD;
2011         }
2012     }
2013     
2014     if (!(G_atmi_env.xa_flags_sys & NDRX_XA_FLAG_SYS_NOJOIN) &&
2015             atmi_xa_is_current_rm_known(p_xai->tmknownrms))
2016     {    
2017         *p_is_known=EXTRUE;
2018         
2019         /* use default btid as in join mode */
2020         G_atmi_tls->G_atmi_xa_curtx.txinfo->btid = 0;
2021         
2022         /* in case if doing resume, then allow the xa_start entry */
2023         if (XA_IS_DYNAMIC_REG && !(join_flag & TMRESUME))
2024         {
2025             NDRX_LOG(log_debug, "Dynamic reg - no start/join!");
2026         }
2027         /* Continue with static ...  ok it is known, then just join the transaction */
2028         else if (EXSUCCEED!=atmi_xa_start_entry(atmi_xa_get_branch_xid(p_xai, 
2029                 G_atmi_tls->G_atmi_xa_curtx.txinfo->btid), 
2030                 join_flag, EXFALSE))
2031         {
2032             NDRX_LOG(log_error, "Failed to join transaction!");
2033         ndrx_xa_join_fail(NULL, EXFALSE);
2034             EXFAIL_OUT(ret);
2035         }
2036         else
2037         {
2038             NDRX_LOG(log_debug, "tx join ok!");
2039         }
2040     }
2041     else
2042     {
2043         long btid = EXFAIL;
2044         NDRX_LOG(log_info, "RM not aware of this transaction");
2045         
2046         /* - if have JOIN, then we use default BTID 0
2047          * - if no JOIN, then ATMI_XA_TMREGISTER will give new TID
2048          */
2049         if (!(G_atmi_env.xa_flags_sys & NDRX_XA_FLAG_SYS_NOJOIN))
2050         {
2051             btid = 0;
2052         }
2053         
2054         if (G_atmi_env.xa_flags_sys & NDRX_XA_FLAG_SYS_NOSTARTXID)
2055         {
2056             tmflags|=TMFLAGS_TPNOSTARTXID;
2057         }
2058 
2059         
2060         /* register new tx branch/rm */
2061         if (NULL==(p_ub=atmi_xa_call_tm_generic(ATMI_XA_TMREGISTER, 
2062                 EXFALSE, EXFAIL, p_xai, tmflags, btid)))
2063         {
2064             NDRX_LOG(log_error, "Failed to execute TM command [%c]", 
2065                         ATMI_XA_TMREGISTER);   
2066             EXFAIL_OUT(ret);
2067         }
2068 
2069         if (EXSUCCEED!=Bget(p_ub, TMTXFLAGS, 0, (char *)&tmflags, 0L))
2070         {
2071             NDRX_LOG(log_error, "Failed to read TMTXFLAGS!");   
2072 
2073             EXFAIL_OUT(ret);
2074         }
2075         
2076         if (tmflags & TMFLAGS_RMIDKNOWN)
2077         {
2078             *p_is_known = EXTRUE;
2079         }
2080         else
2081         {
2082             if (EXSUCCEED!=Bget(p_ub, TMTXBTID, 0, (char *)&btid, 0L))
2083             {
2084                 NDRX_LOG(log_error, "Failed to read TMTXBTID!");   
2085 
2086                 EXFAIL_OUT(ret);
2087             }
2088         }
2089         
2090         G_atmi_tls->G_atmi_xa_curtx.txinfo->btid = btid;
2091 
2092         if (XA_IS_DYNAMIC_REG)
2093         {
2094             /* really? how RM knows about our XID? */
2095             NDRX_LOG(log_debug, "Dynamic reg - no new tx start!");
2096         }
2097         /* Continue with static... */
2098         else if (*p_is_known)
2099         {
2100             if (EXSUCCEED!=atmi_xa_start_entry(atmi_xa_get_branch_xid(p_xai, btid), 
2101                     join_flag, EXFALSE))
2102             {
2103                 NDRX_LOG(log_error, "Failed to join transaction!");
2104         ndrx_xa_join_fail(NULL, EXFALSE);
2105                 EXFAIL_OUT(ret);
2106             }
2107             else
2108             {
2109                 NDRX_LOG(log_debug, "tx join ok!");
2110             }
2111         }
2112         /* Open new transaction in branch */
2113         else if (EXSUCCEED!=atmi_xa_start_entry(atmi_xa_get_branch_xid(p_xai, btid), 
2114                 TMNOFLAGS, EXTRUE)) /* silent attempt...*/
2115         {
2116             reason=atmi_xa_get_reason();
2117             NDRX_LOG(log_error, "Failed to create new tx under local RM (reason: %hd): %s!", 
2118                     reason, atmi_xa_geterrstr(reason));
2119             if (XAER_DUPID == (reason=atmi_xa_get_reason()))
2120             {
2121                 /* It is already known... then join... */
2122                 *p_is_known=EXTRUE;
2123                 
2124                 if (EXSUCCEED!=atmi_xa_start_entry(atmi_xa_get_branch_xid(p_xai, btid), 
2125                         join_flag, EXFALSE))
2126                 {
2127                     NDRX_LOG(log_error, "Failed to join transaction!");
2128             ndrx_xa_join_fail(NULL, EXFALSE);
2129                     EXFAIL_OUT(ret);
2130                 }
2131                 else
2132                 {
2133                     NDRX_LOG(log_debug, "tx join ok!");
2134                 }
2135             }
2136             else
2137             {
2138                 NDRX_LOG(log_error, "Failed to start transaction!");
2139                 ndrx_xa_join_fail(NULL, EXFALSE);
2140                 EXFAIL_OUT(ret);
2141             }
2142         }
2143         new_rm = EXTRUE;
2144     }
2145         
2146     
2147     if (!(G_atmi_env.xa_flags_sys & NDRX_XA_FLAG_SYS_NOJOIN) && new_rm)
2148     {
2149         src_tmknownrms[0] = G_atmi_env.xa_rmid;
2150         src_tmknownrms[1] = EXEOS;
2151         
2152         if (EXSUCCEED!=atmi_xa_update_known_rms(
2153                 G_atmi_tls->G_atmi_xa_curtx.txinfo->tmknownrms, 
2154                 src_tmknownrms))
2155         {
2156             EXFAIL_OUT(ret);
2157         }
2158     }
2159     
2160 out:
2161 
2162     if (EXSUCCEED!=ret)
2163     {
2164         /* Remove current, if was set... */
2165         atmi_xa_reset_curtx();
2166     }
2167 
2168     if (NULL!=p_ub)
2169     {
2170         tpfree((char *)p_ub);
2171     }
2172 
2173     return ret;
2174 }
2175 
2176 /**
2177  * Disassociate current process from transaction 
2178  * TODO: What about CD's?
2179  * @param force_rollback shall we rollback the transaction?
2180  * @param end_fail did the end failed?
2181  * @return EXSUCCEED/EXFAIL
2182  */
2183 expublic int _tp_srv_disassoc_tx(int force_rollback, int *end_fail)
2184 {
2185     int ret = EXSUCCEED;
2186     ATMI_TLS_ENTRY;
2187     
2188     
2189     NDRX_LOG(log_debug, "into %s() force_rollback=%d", __func__, force_rollback);
2190     if (NULL==G_atmi_tls->G_atmi_xa_curtx.txinfo)
2191     {
2192         NDRX_LOG(log_warn, "Not in global tx!");
2193         goto out;
2194     }
2195     
2196     /* Only for static...  or if work done */
2197     if ( !XA_IS_DYNAMIC_REG || 
2198             (XA_TXINFO_AXREG_CLD & G_atmi_tls->G_atmi_xa_curtx.txinfo->tranid_flags))
2199     {
2200         if (EXSUCCEED!= (ret=atmi_xa_end_entry(
2201                 atmi_xa_get_branch_xid(G_atmi_tls->G_atmi_xa_curtx.txinfo,
2202                 G_atmi_tls->G_atmi_xa_curtx.txinfo->btid), TMSUCCESS, EXFALSE)))
2203         {
2204             NDRX_LOG(log_error, "Failed to end XA api: %d [%s]", 
2205                     ret, atmi_xa_geterrstr(ret));
2206             userlog("Failed to end XA api: %d [%s]", 
2207                     ret, atmi_xa_geterrstr(ret));
2208             
2209             *end_fail = EXTRUE;
2210         }
2211     }
2212     
2213     /* rollback the transaction if required */
2214     if (force_rollback)
2215     {
2216         ndrx_xa_join_fail(NULL, EXTRUE);
2217     }
2218 
2219     /* Remove current transaction from list */
2220     atmi_xa_curtx_del(G_atmi_tls->G_atmi_xa_curtx.txinfo);    
2221     G_atmi_tls->G_atmi_xa_curtx.txinfo = NULL;
2222     
2223 out:
2224     return ret;
2225 }
2226 
2227 /**
2228  * Tell master TM that current transaction have been failed.
2229  * (so that TM can mark transaction as abort only)
2230  * @return SUCCEED/FAIL
2231  */
2232 expublic int _tp_srv_tell_tx_fail(void)
2233 {
2234     int ret = EXSUCCEED;
2235     
2236     /* TODO: */
2237     
2238 out:
2239     return ret;
2240 }
2241 
2242 /**
2243  * Disable suspend at context switching
2244  * @param val EXTRUE/EXFALSE
2245  */
2246 expublic void ndrx_xa_noapisusp(int val)
2247 {
2248     if (val)
2249     {
2250         NDRX_LOG(log_debug, "No Context tran suspend");
2251         G_atmi_env.xa_flags_sys|=NDRX_XA_FLAG_SYS_NOAPISUSP;
2252     }
2253     else
2254     {
2255         G_atmi_env.xa_flags_sys=G_atmi_env.xa_flags_sys & ~NDRX_XA_FLAG_SYS_NOAPISUSP;
2256     }
2257 }
2258 
2259 /**
2260  * XA Driver does not support join call
2261  * Such us Mysql/Posgresql
2262  * @param val EXTRUE/EXFALSE
2263  */
2264 expublic void ndrx_xa_nojoin(int val)
2265 {
2266     if (val)
2267     {
2268         NDRX_LOG(log_debug, "XA No JOIN");
2269         G_atmi_env.xa_flags_sys|=NDRX_XA_FLAG_SYS_NOJOIN;
2270     }
2271     else
2272     {
2273         G_atmi_env.xa_flags_sys=G_atmi_env.xa_flags_sys & ~NDRX_XA_FLAG_SYS_NOJOIN;
2274     }
2275 }
2276 
2277 /**
2278  * Do not suspend transaction
2279  * This is basically optimizatoin to avoid overhead for resources which does not require
2280  * suspend
2281  * @param val EXTRUE/EXFALSE
2282  */
2283 expublic void ndrx_xa_nosuspend(int val)
2284 {
2285     if (val)
2286     {
2287         NDRX_LOG(log_debug, "XA No Automatic suspend");
2288         G_atmi_env.xa_flags_sys|=NDRX_XA_FLAG_SYS_NOSUSPEND;
2289     }
2290     else
2291     {
2292         G_atmi_env.xa_flags_sys=G_atmi_env.xa_flags_sys & ~NDRX_XA_FLAG_SYS_NOSUSPEND;
2293     }
2294 }
2295 
2296 /**
2297  * XA Driver does not mark the transaction at start, thus no start XID
2298  * Thus in this case before process calls "end", the prepare statement will be
2299  * issued. Also when we request the new BTID, we shall report, that branch
2300  * is in prepared state...
2301  * @param val EXTRUE/EXFALSE
2302  */
2303 expublic void ndrx_xa_nostartxid(int val)
2304 {
2305     if (val)
2306     {
2307         NDRX_LOG(log_debug, "XA No STAR XID");
2308         G_atmi_env.xa_flags_sys|=NDRX_XA_FLAG_SYS_NOSTARTXID;
2309     }
2310     else
2311     {
2312         G_atmi_env.xa_flags_sys=G_atmi_env.xa_flags_sys & ~NDRX_XA_FLAG_SYS_NOSTARTXID;
2313     }
2314 }
2315 
2316 /**
2317  * Set local abort function. Recommended for NOSTARTXID to avoid prepare before
2318  * abort. Set by XA Driver.
2319  * @param pf_xa_loctxabort NULL or ptr to local transaction abort function.
2320  */
2321 expublic void ndrx_xa_setloctxabort(int (*pf_xa_loctxabort)(XID *xid, long flags))
2322 {
2323     G_atmi_env.pf_xa_loctxabort = pf_xa_loctxabort;
2324     NDRX_LOG(log_debug, "xa_loctxabort set to %p", G_atmi_env.pf_xa_loctxabort);
2325 }
2326 
2327 
2328 /**
2329  * Set function for returning current connection object.
2330  * This might be used for custom XA Switches
2331  * @param pf_xa_getconn Callback for requesting the connection object
2332  */
2333 expublic void ndrx_xa_setgetconnn(void *(*pf_xa_getconn)(void))
2334 {
2335     G_atmi_env.pf_getconn= pf_xa_getconn;
2336     NDRX_LOG(log_debug, "pf_getconn set to %p", G_atmi_env.pf_getconn);
2337 }
2338 
2339 /**
2340  * Set tight branching flag
2341  * @param val EXTRUE/EXFALSE
2342  */
2343 expublic void ndrx_xa_btight(int val)
2344 {
2345     if (val)
2346     {
2347         NDRX_LOG(log_debug, "XA BTIGHT");
2348         G_atmi_env.xa_flags_sys|=NDRX_XA_FLAG_SYS_BTIGHT;
2349     }
2350     else
2351     {
2352         G_atmi_env.xa_flags_sys=G_atmi_env.xa_flags_sys & ~NDRX_XA_FLAG_SYS_BTIGHT;
2353     }
2354 }
2355 
2356 /**
2357  * Get current XA switch
2358  * @return currently loaded XA switch
2359  */
2360 expublic struct xa_switch_t* ndrx_xa_sw_get(void)
2361 {
2362     return G_atmi_env.xa_sw;
2363 }
2364 
2365 /* vim: set ts=4 sw=4 et smartindent: */