Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief Tmsrv server - transaction monitor
0003  *   After that log transaction to hash & to disk for tracking the stuff...
0004  *   TODO: We should have similar control like "TP_COMMIT_CONTROL" -
0005  *   either return after stuff logged or after really commit completed.
0006  *   Error handling:
0007  *   - System errors we will track via ATMI interface error functions
0008  *   - XA errors will be tracked via XA error interface
0009  *
0010  * @file tmapi.c
0011  */
0012 /* -----------------------------------------------------------------------------
0013  * Enduro/X Middleware Platform for Distributed Transaction Processing
0014  * Copyright (C) 2009-2016, ATR Baltic, Ltd. All Rights Reserved.
0015  * Copyright (C) 2017-2023, Mavimax, Ltd. All Rights Reserved.
0016  * This software is released under one of the following licenses:
0017  * AGPL (with Java and Go exceptions) or Mavimax's license for commercial use.
0018  * See LICENSE file for full text.
0019  * -----------------------------------------------------------------------------
0020  * AGPL license:
0021  *
0022  * This program is free software; you can redistribute it and/or modify it under
0023  * the terms of the GNU Affero General Public License, version 3 as published
0024  * by the Free Software Foundation;
0025  *
0026  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0027  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0028  * PARTICULAR PURPOSE. See the GNU Affero General Public License, version 3
0029  * for more details.
0030  *
0031  * You should have received a copy of the GNU Affero General Public License along 
0032  * with this program; if not, write to the Free Software Foundation, Inc.,
0033  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0034  *
0035  * -----------------------------------------------------------------------------
0036  * A commercial use license is available from Mavimax, Ltd
0037  * contact@mavimax.com
0038  * -----------------------------------------------------------------------------
0039  */
0040 #include <stdio.h>
0041 #include <stdlib.h>
0042 #include <string.h>
0043 #include <errno.h>
0044 #include <regex.h>
0045 #include <utlist.h>
0046 
0047 #include <ndebug.h>
0048 #include <atmi.h>
0049 #include <atmi_int.h>
0050 #include <typed_buf.h>
0051 #include <ndrstandard.h>
0052 #include <ubf.h>
0053 #include <Exfields.h>
0054 
0055 #include <exnet.h>
0056 #include <ndrxdcmn.h>
0057 
0058 #include "tmsrv.h"
0059 #include "../libatmisrv/srv_int.h"
0060 #include "tperror.h"
0061 #include <xa_cmn.h>
0062 #include <exbase64.h>
0063 /*---------------------------Externs------------------------------------*/
0064 /*---------------------------Macros-------------------------------------*/
0065 #define XID_RECOVER_BLOCK_SZ        500
0066 /*---------------------------Enums--------------------------------------*/
0067 /*---------------------------Typedefs-----------------------------------*/
0068 /*---------------------------Globals------------------------------------*/
0069 /*---------------------------Statics------------------------------------*/
0070 /*---------------------------Prototypes---------------------------------*/
0071 
0072 /******************************************************************************/
0073 /*                               TP API COMMANDS                              */
0074 /******************************************************************************/
0075 
0076 /**
0077  * TP API
0078  * TP entry for abort.
0079  * @param p_ub
0080  * @return 
0081  */
0082 expublic int tm_tpabort(UBFH *p_ub)
0083 {
0084     int ret = EXSUCCEED;
0085     atmi_xa_tx_info_t xai;
0086     atmi_xa_log_t *p_tl = NULL;
0087     int locke;
0088     
0089     NDRX_LOG(log_debug, "tm_tpabort() called");
0090     
0091     /* 1. get transaction from hash */
0092     if (EXSUCCEED!=atmi_xa_read_tx_info(p_ub, &xai, XA_TXINFO_NOBTID))
0093     {
0094         NDRX_LOG(log_error, "Failed to read transaction data");
0095         /* - will assume that completed OK!
0096         atmi_xa_set_error_msg(p_ub, TPESYSTEM, NDRX_XA_ERSN_INVPARAM, 
0097                 "Invalid transaction data (missing fields)");
0098         */
0099         atmi_xa_set_error_fmt(p_ub, TPEINVAL, NDRX_XA_ERSN_NOTX, 
0100                 "Transaction with xid [%s] not logged - probably was tout+abort", 
0101                 xai.tmxid);
0102         ret=EXSUCCEED;
0103         goto out;
0104     }
0105     
0106     /* read tx from hash */
0107     if (NULL==(p_tl = tms_log_get_entry(xai.tmxid, NDRX_LOCK_WAIT_TIME, &locke)))
0108     {
0109         if (locke)
0110         {
0111             NDRX_LOG(log_error, "Lock xid [%s] timed out", 
0112                     xai.tmxid);
0113             atmi_xa_set_error_fmt(p_ub, TPETIME, NDRX_XA_ERSN_LOCK, 
0114                     "Lock xid [%s] timed out", xai.tmxid);
0115         }
0116         else
0117         {
0118             NDRX_LOG(log_error, "Transaction with xid [%s] not logged", 
0119                     xai.tmxid);
0120             atmi_xa_set_error_fmt(p_ub, TPEPROTO, NDRX_XA_ERSN_NOTX, 
0121                     "Transaction with xid [%s] not logged", xai.tmxid);
0122         }
0123         EXFAIL_OUT(ret);
0124     }
0125     
0126     /* if processing in background (say time-out rollback, the commit shall not be
0127      * accepted)
0128      */
0129     if (p_tl->is_background || XA_TX_STAGE_ACTIVE!=p_tl->txstage)
0130     {
0131         userlog("Cannot abort xid [%s] is_background: (%d) stage: (%hd)", 
0132                 xai.tmxid, p_tl->is_background, p_tl->txstage);
0133         NDRX_LOG(log_error, "Cannot abort xid [%s] is_background: (%d) stage: (%hd)", 
0134                 xai.tmxid, p_tl->is_background, p_tl->txstage);
0135         
0136         if (p_tl->txstage >=XA_TX_STAGE_PREPARING)
0137         {            
0138             atmi_xa_set_error_fmt(p_ub, TPEPROTO, NDRX_XA_ERSN_INPROGRESS, 
0139                 "Cannot abort xid [%s] is_background: (%d) stage: (%hd) (>=preparing)", 
0140                 xai.tmxid, p_tl->is_background, p_tl->txstage);
0141         }
0142         else
0143         {
0144             /* assume completed heuristically (probably was time-out) */
0145             atmi_xa_set_error_fmt(p_ub, TPEHEURISTIC, NDRX_XA_ERSN_INPROGRESS, 
0146                 "xid [%s] is_background: (%d) stage: (%hd) (is aborting)", 
0147                 xai.tmxid, p_tl->is_background, p_tl->txstage);
0148         }
0149         
0150         tms_unlock_entry(p_tl);
0151         
0152         EXFAIL_OUT(ret);
0153     }
0154     
0155     /* Switch the state to aborting... */
0156     tms_log_stage(p_tl, XA_TX_STAGE_ABORTING, EXTRUE);
0157     
0158     /* Call internal version of abort */
0159     if (EXSUCCEED!=(ret=tm_drive(&xai, p_tl, XA_OP_ROLLBACK, EXFAIL, 0L)))
0160     {
0161         atmi_xa_set_error_fmt(p_ub, ret, NDRX_XA_ERSN_RMCOMMITFAIL, 
0162                 "Distributed transaction process did not finish completely");
0163         ret = EXSUCCEED;
0164     }
0165         
0166 out:
0167         
0168     return ret;
0169 }
0170 
0171 /**
0172  * TP API
0173  * Commit the transaction (master entry)
0174  * Transaction initiator ask to commit the global transaction.
0175  * @param p_ub
0176  * @return 
0177  */
0178 expublic int tm_tpcommit(UBFH *p_ub)
0179 {
0180     int ret = EXSUCCEED;
0181     atmi_xa_tx_info_t xai;
0182     atmi_xa_log_t *p_tl = NULL;
0183     int do_abort = EXFALSE;
0184     long tmflags;
0185     int locke;
0186     NDRX_LOG(log_debug, "tm_tpcommit() called");
0187     
0188     /* 1. get transaction from hash */
0189     if (EXSUCCEED!=atmi_xa_read_tx_info(p_ub, &xai, XA_TXINFO_NOBTID))
0190     {
0191         NDRX_LOG(log_error, "Failed to read transaction data");
0192         atmi_xa_set_error_msg(p_ub, TPESYSTEM, NDRX_XA_ERSN_INVPARAM, 
0193                 "Invalid transaction data (missing fields)");
0194         EXFAIL_OUT(ret);
0195     }
0196     
0197     /* tx.h support: */
0198     if (EXSUCCEED!=Bget(p_ub, TMTXFLAGS, 0, (char *)&tmflags, 0L))
0199     {
0200         NDRX_LOG(log_error, "Failed to get TMTXFLAGS: %s", Bstrerror(Berror));
0201         atmi_xa_set_error_msg(p_ub, TPESYSTEM, NDRX_XA_ERSN_INVPARAM, 
0202                 "Failed to read TMTXFLAGS");
0203         EXFAIL_OUT(ret);
0204     }
0205     
0206     /* read tx from hash */
0207     if (NULL==(p_tl = tms_log_get_entry(xai.tmxid, NDRX_LOCK_WAIT_TIME, &locke)))
0208     {
0209         if (locke)
0210         {
0211             NDRX_LOG(log_error, "Lock xid [%s] timed out", 
0212                     xai.tmxid);
0213             atmi_xa_set_error_fmt(p_ub, TPETIME, NDRX_XA_ERSN_LOCK, 
0214                     "Lock xid [%s] timed out", xai.tmxid);   
0215         }
0216         else
0217         {
0218             NDRX_LOG(log_error, "Transaction with xid [%s] not logged", 
0219                     xai.tmxid);
0220             atmi_xa_set_error_fmt(p_ub, TPEABORT, NDRX_XA_ERSN_NOTX, 
0221                     "Transaction with xid [%s] not logged - probably was tout+abort", 
0222                     xai.tmxid);
0223         }
0224         EXFAIL_OUT(ret);
0225     }
0226     
0227     /* if processing in background (say time-out rollback, the commit shall not be
0228      * accepted)
0229      */
0230     if (p_tl->is_background || XA_TX_STAGE_ACTIVE!=p_tl->txstage)
0231     {
0232         userlog("Cannot commit xid [%s] is_background: (%d) stage: (%hd)", 
0233                 xai.tmxid, p_tl->is_background, p_tl->txstage);
0234         NDRX_LOG(log_error, "Cannot commit xid [%s] is_background: (%d) stage: (%hd)", 
0235                 xai.tmxid, p_tl->is_background, p_tl->txstage);
0236         if (p_tl->txstage >=XA_TX_STAGE_PREPARING )
0237         {
0238             atmi_xa_set_error_fmt(p_ub, TPEPROTO, NDRX_XA_ERSN_INPROGRESS, 
0239                     "Cannot commit xid [%s] is_background: (%d) stage: (%hd) (>=is preparing)", 
0240                     xai.tmxid, p_tl->is_background, p_tl->txstage);
0241         }
0242         else
0243         {
0244             atmi_xa_set_error_fmt(p_ub, TPEABORT, NDRX_XA_ERSN_INPROGRESS, 
0245                     "Cannot commit xid [%s] is_background: (%d) stage: (%hd) (is aborting)", 
0246                     xai.tmxid, p_tl->is_background, p_tl->txstage);
0247         }
0248         
0249         tms_unlock_entry(p_tl);
0250         
0251         EXFAIL_OUT(ret);
0252     }
0253     
0254     /* Open log file 
0255      * - now we open the file at the start of the transaction.
0256     if (EXSUCCEED!=tms_open_logfile(p_tl, "w"))
0257     {
0258         NDRX_LOG(log_error, "Failed to open log file");
0259         atmi_xa_set_error_msg(p_ub, TPESYSTEM, NDRX_XA_ERSN_INVPARAM, 
0260                 "Failed to open log file");
0261         do_abort = EXTRUE;
0262         EXFAIL_OUT(ret);
0263     }
0264     */
0265     
0266     /* Log that we start commit... */
0267     if (EXSUCCEED!=tms_log_stage(p_tl, XA_TX_STAGE_PREPARING, EXFALSE))
0268     {
0269         NDRX_LOG(log_error, "Failed to log preparing stage");
0270         do_abort = EXTRUE;
0271         atmi_xa_set_error_fmt(p_ub, TPEABORT, NDRX_XA_ERSN_LOGFAIL, 
0272                     "Failed to log preparing stage");
0273         EXFAIL_OUT(ret);
0274     }
0275     
0276     /* Drive - it will auto-rollback if needed... */
0277     if (EXSUCCEED!=(ret=tm_drive(&xai, p_tl, XA_OP_COMMIT, EXFAIL, tmflags)))
0278     {
0279         atmi_xa_set_error_msg(p_ub, ret, NDRX_XA_ERSN_RMCOMMITFAIL, 
0280                 "Transaction did not complete fully");
0281         ret=EXFAIL;
0282         goto out;
0283     }
0284     
0285 out:
0286                 
0287     /* Rollback the work done! */
0288     if (do_abort)
0289     {
0290         NDRX_LOG(log_warn, "About to rollback transaction!");
0291         
0292         tms_log_stage(p_tl, XA_TX_STAGE_ABORTING, EXTRUE);
0293         
0294         /* Call internal version of abort */
0295         if (EXSUCCEED!=(ret=tm_drive(&xai, p_tl, XA_OP_ROLLBACK, EXFAIL, 0L)))
0296         {
0297             atmi_xa_override_error(p_ub, ret);
0298             ret=EXFAIL;
0299         }
0300         
0301     }
0302 
0303     return ret;
0304 }
0305 
0306 /**
0307  * TP API
0308  * Start new XA transaction
0309  * @param p_ub
0310  * @return 
0311  */
0312 expublic int tm_tpbegin(UBFH *p_ub)
0313 {
0314     int ret=EXSUCCEED;
0315     XID xid; /* handler for new XID */
0316     atmi_xa_tx_info_t xai;
0317     int do_rollback=EXFALSE;
0318     char xid_str[NDRX_XID_SERIAL_BUFSIZE];
0319     long txtout;
0320     long tmflags;
0321     long btid = EXFAIL;
0322     NDRX_LOG(log_debug, "tm_tpbegin() called");
0323     
0324     if (EXSUCCEED!=Bget(p_ub, TMTXFLAGS, 0, (char *)&tmflags, 0L))
0325     {
0326         NDRX_LOG(log_error, "Failed to read TMTXFLAGS!");
0327         EXFAIL_OUT(ret);   
0328     }
0329     
0330     atmi_xa_new_xid(&xid);
0331     
0332     xai.tmknownrms[0] = 0;
0333     
0334     /* we should start new transaction... (only if static...) */
0335     if (!(tmflags & TMFLAGS_DYNAMIC_REG))
0336     {
0337         /*
0338         if (EXSUCCEED!=(ret = atmi_xa_start_entry(&xid, 0, EXFALSE)))
0339         {
0340             NDRX_LOG(log_error, "Failed to start new transaction!");
0341             atmi_xa_set_error_fmt(p_ub, TPETRAN, NDRX_XA_ERSN_NONE, 
0342                     "Failed to start new transaction, "
0343                     "xa error: %d [%s]", ret, atmi_xa_geterrstr(ret));
0344             goto out;
0345         }
0346         */
0347         
0348         /* We know it only if it is not BTID resource */
0349         if (!(tmflags & TMFLAGS_TPNOSTARTXID))
0350         {
0351             xai.tmknownrms[0] = G_atmi_env.xa_rmid;
0352             xai.tmknownrms[1] = EXEOS;
0353         }
0354     }
0355 
0356     atmi_xa_serialize_xid(&xid, xid_str);
0357     
0358     /* load the XID into buffer */
0359     NDRX_STRCPY_SAFE(xai.tmxid, xid_str);
0360     xai.tmrmid = G_atmi_env.xa_rmid;
0361     xai.tmnodeid = G_tmsrv_cfg.vnodeid;
0362     xai.tmsrvid = G_server_conf.srv_id;
0363     
0364     /* Currently time-out is handled only locally by TM */
0365     if (EXSUCCEED!=Bget(p_ub, TMTXTOUT, 0, (char *)&txtout, 0L) || 0>=txtout)
0366     {
0367         txtout = G_tmsrv_cfg.dflt_timeout;
0368         NDRX_LOG(log_debug, "TX tout defaulted to: %ld", txtout);
0369     }
0370     else
0371     {
0372         NDRX_LOG(log_debug, "TX tout: %ld", txtout);
0373     }
0374     
0375     /* Only for static...
0376     if (!(tmflags & TMTXFLAGS_DYNAMIC_REG))
0377     {
0378         if (EXSUCCEED!=(ret = atmi_xa_end_entry(&xid, TMSUCCESS)))
0379         {
0380             NDRX_LOG(log_error, "Failed to end XA api!");
0381             atmi_xa_set_error_fmt(p_ub, TPETRAN, NDRX_XA_ERSN_NONE, 
0382                     "Failed to start end transaction, "
0383                     "xa error: %d [%s]", ret, atmi_xa_geterrstr(ret));
0384             goto out;
0385         }
0386     }
0387     */
0388     
0389     if (EXSUCCEED!=atmi_xa_load_tx_info(p_ub, &xai))
0390     {
0391         NDRX_LOG(log_error, "Failed to load tx info!");
0392         atmi_xa_set_error_fmt(p_ub, TPETRAN, NDRX_XA_ERSN_NONE, 
0393                     "Failed to return tx info!");
0394         do_rollback = EXTRUE;
0395         ret=EXFAIL;
0396         goto out;
0397     }
0398     
0399     /* Log into journal */
0400     if (EXSUCCEED!=tms_log_start(&xai, txtout, tmflags, &btid))
0401     {
0402         NDRX_LOG(log_error, "Failed to log the transaction!");
0403         atmi_xa_set_error_fmt(p_ub, TPETRAN, NDRX_XA_ERSN_LOGFAIL, 
0404                     "Failed to log the transaction!");
0405         do_rollback = EXTRUE;
0406         ret=EXFAIL;
0407         goto out;
0408     }
0409     
0410     /* TID to log */
0411     if (EXSUCCEED!=Bchg(p_ub, TMTXBTID, 0, (char *)&btid, 0L))
0412     {
0413         NDRX_LOG(log_error, "Failed to set TMTXBTID: %s", Bstrerror(Berror));
0414         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_UBFERR, 
0415                     "Failed to set TMTXBTID: %s", Bstrerror(Berror));
0416         
0417         /* How about closing log the file? */
0418         do_rollback = EXTRUE;
0419         ret=EXFAIL;
0420         goto out;
0421     }
0422     
0423 out:
0424     
0425     /* We should abort the transaction right now */
0426     if (do_rollback)
0427     {
0428         ret = tm_rollback_local(p_ub, &xai, btid);
0429     }
0430 
0431     return ret;
0432 }
0433 
0434 /******************************************************************************/
0435 /*                         TRANSACTION BRANCH COMMANDS                        */
0436 /******************************************************************************/
0437 
0438 /**
0439  * Register resource manager under given transaction
0440  * @param p_ub
0441  * @return 
0442  */
0443 expublic int tm_tmregister(UBFH *p_ub)
0444 {
0445     int ret = EXSUCCEED;
0446     short   callerrm;
0447     int is_already_logged = EXFALSE;
0448     atmi_xa_tx_info_t xai;
0449     long tmflags = 0;
0450     long btid=EXFAIL;
0451     
0452     
0453     /* TODO: Get flags! */
0454     
0455     if (EXSUCCEED!=Bget(p_ub, TMCALLERRM, 0, (char *)&callerrm, 0L))
0456     {
0457         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_INVPARAM, 
0458                     "Missing TMCALLERRM field: %s!", Bstrerror(Berror));
0459         EXFAIL_OUT(ret);
0460     }
0461     
0462     if (EXSUCCEED!=atmi_xa_read_tx_info(p_ub, &xai, XA_TXINFO_NOBTID))
0463     {
0464         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_INVPARAM, 
0465                     "Failed to read transaction info!");
0466         EXFAIL_OUT(ret);
0467     }
0468     
0469     /* read BTID (if have one in their side) */
0470     
0471     if (EXSUCCEED!=Bget(p_ub, TMTXBTID, 0, (char *)&btid, NULL) && Berror!=BNOTPRES)
0472     {
0473         NDRX_LOG(log_error, "Failed to get TMTXBTID: %s", Bstrerror(Berror));
0474         
0475         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_UBFERR, 
0476                      "Failed to get TMTXBTID: %s", Bstrerror(Berror));
0477         EXFAIL_OUT(ret);
0478     }
0479     
0480     if (EXSUCCEED!=Bget(p_ub, TMTXFLAGS, 0, (char *)&tmflags, 0L))
0481     {
0482         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_INVPARAM, 
0483                     "Missing TMTXFLAGS in buffer");
0484         EXFAIL_OUT(ret);
0485     }
0486     
0487     if (EXSUCCEED!=tms_log_addrm(&xai, callerrm, &is_already_logged, &btid, tmflags))
0488     {
0489         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_RMLOGFAIL, 
0490                     "Failed to log new RM!");
0491         EXFAIL_OUT(ret);
0492     }
0493     
0494     if (is_already_logged)
0495     {
0496         tmflags|=TMFLAGS_RMIDKNOWN;
0497     }
0498     
0499     /* return new TID */
0500     if (!Bpres(p_ub, TMTXBTID, 0) &&
0501             EXSUCCEED!=Bchg(p_ub, TMTXBTID, 0, (char *)&btid, 0L))
0502     {
0503         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_UBFERR, 
0504                     "Failed to set TMTXBTID!");
0505         EXFAIL_OUT(ret);
0506     }
0507     
0508     if (EXSUCCEED!=Bchg(p_ub, TMTXFLAGS, 0, (char *)&tmflags, 0L))
0509     {
0510         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_UBFERR, 
0511                     "Failed to set TMTXFLAGS!");
0512         EXFAIL_OUT(ret);
0513     }
0514     
0515 out:
0516     return ret; 
0517 }
0518 
0519 /**
0520  * Report transaction status of the branch
0521  * @param p_ub
0522  * @return 
0523  */
0524 expublic int tm_rmstatus(UBFH *p_ub)
0525 {
0526     int ret = EXSUCCEED;
0527     short   callerrm;
0528     int is_already_logged = EXFALSE;
0529     atmi_xa_tx_info_t xai;
0530     long tmflags = 0;
0531     long btid=EXFAIL;
0532     char rmstatus;
0533     
0534     
0535     /* TODO: Get flags! */
0536     
0537     if (EXSUCCEED!=Bget(p_ub, TMCALLERRM, 0, (char *)&callerrm, 0L))
0538     {
0539         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_INVPARAM, 
0540                     "Missing TMCALLERRM field: %s!", Bstrerror(Berror));
0541         EXFAIL_OUT(ret);
0542     }
0543     
0544     if (EXSUCCEED!=atmi_xa_read_tx_info(p_ub, &xai, XA_TXINFO_NOBTID))
0545     {
0546         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_INVPARAM, 
0547                     "Failed to read transaction info!");
0548         EXFAIL_OUT(ret);
0549     }
0550     
0551     /* read BTID (if have one in their side) */
0552     
0553     if (EXSUCCEED!=Bget(p_ub, TMTXBTID, 0, (char *)&btid, NULL))
0554     {
0555         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_INVPARAM, 
0556                     "Missing TMTXBTID!");
0557         EXFAIL_OUT(ret);
0558     }
0559     
0560     if (EXSUCCEED!=Bget(p_ub, TMTXRMSTATUS, 0, (char *)&rmstatus, 0L))
0561     {
0562         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_INVPARAM, 
0563                     "Missing TMTXRMSTATUS in buffer");
0564         EXFAIL_OUT(ret);
0565     }
0566 
0567     if (EXSUCCEED!=tms_log_chrmstat(&xai, callerrm, btid, rmstatus, p_ub))
0568     {
0569         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_RMLOGFAIL, 
0570                     "Failed to log new RM!");
0571         EXFAIL_OUT(ret);
0572     }
0573     
0574     if (is_already_logged)
0575     {
0576         tmflags|=TMFLAGS_RMIDKNOWN;
0577     }
0578     
0579     /* return new TID */
0580     if (!Bpres(p_ub, TMTXBTID, 0) &&
0581             EXSUCCEED!=Bchg(p_ub, TMTXBTID, 0, (char *)&btid, 0L))
0582     {
0583         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_UBFERR, 
0584                     "Failed to set TMTXBTID!");
0585         EXFAIL_OUT(ret);
0586     }
0587     
0588     if (EXSUCCEED!=Bchg(p_ub, TMTXFLAGS, 0, (char *)&tmflags, 0L))
0589     {
0590         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_UBFERR, 
0591                     "Failed to set TMTXFLAGS!");
0592         EXFAIL_OUT(ret);
0593     }
0594     
0595 out:
0596     return ret; 
0597 }
0598 
0599 
0600 /**
0601  * Do the internal prepare of transaction (request sent from other TM)
0602  * @param p_ub
0603  * @return 
0604  */
0605 expublic int tm_tmprepare(UBFH *p_ub)
0606 {
0607     int ret = EXSUCCEED;
0608     atmi_xa_tx_info_t xai;
0609     
0610     NDRX_LOG(log_debug, "tm_tmprepare called.");
0611     /* read xai from FB... */
0612     if (EXSUCCEED!=atmi_xa_read_tx_info(p_ub, &xai, 0))
0613     {
0614         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_INVPARAM, 
0615                     "Failed to read transaction info!");
0616         EXFAIL_OUT(ret);
0617     }
0618     
0619     if (EXSUCCEED!=(ret = tm_prepare_local(p_ub, &xai, xai.btid)))
0620     {
0621         EXFAIL_OUT(ret);
0622     }
0623     
0624 out:
0625     return ret;
0626 }
0627 
0628 /**
0629  * Do the internal commit of transaction (request sent from other TM)
0630  * @param p_ub
0631  * @return 
0632  */
0633 expublic int tm_tmcommit(UBFH *p_ub)
0634 {
0635     int ret = EXSUCCEED;
0636     atmi_xa_tx_info_t xai;
0637     
0638     NDRX_LOG(log_debug, "tm_tmcommit called.");
0639     /* read xai from FB... */
0640     if (EXSUCCEED!=atmi_xa_read_tx_info(p_ub, &xai, 0))
0641     {
0642         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_INVPARAM, 
0643                     "Failed to read transaction info!");
0644         EXFAIL_OUT(ret);
0645     }
0646     
0647     if (EXSUCCEED!=(ret = tm_commit_local(p_ub, &xai, xai.btid)))
0648     {
0649         EXFAIL_OUT(ret);
0650     }
0651     
0652 out:
0653     return ret;
0654 }
0655 
0656 /**
0657  * Local transaction branch abort command. Sent from Master TM
0658  * @param p_ub
0659  * @return 
0660  */
0661 expublic int tm_tmabort(UBFH *p_ub)
0662 {
0663     int ret = EXSUCCEED;
0664     atmi_xa_tx_info_t xai;
0665     
0666     NDRX_LOG(log_debug, "tm_tmabort called.");
0667     if (EXSUCCEED!=atmi_xa_read_tx_info(p_ub, &xai, 0))
0668     {
0669         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_INVPARAM, 
0670                     "Failed to read transaction info!");
0671         EXFAIL_OUT(ret);
0672     }
0673     
0674     /* read xai from FB... */
0675     if (EXSUCCEED!=(ret = tm_rollback_local(p_ub, &xai, xai.btid)))
0676     {
0677         EXFAIL_OUT(ret);
0678     }
0679     
0680 out:
0681     return ret;
0682 }
0683 
0684 /**
0685  * Forget entry
0686  * @param p_ub req buf
0687  * @return EXSUCCEED/EXFAIL
0688  */
0689 expublic int tm_tmforget(UBFH *p_ub)
0690 {
0691     int ret = EXSUCCEED;
0692     atmi_xa_tx_info_t xai;
0693     
0694     NDRX_LOG(log_debug, "tm_tmforget called.");
0695     if (EXSUCCEED!=atmi_xa_read_tx_info(p_ub, &xai, 0))
0696     {
0697         atmi_xa_set_error_fmt(p_ub, TPESYSTEM, NDRX_XA_ERSN_INVPARAM, 
0698                     "Failed to read transaction info!");
0699         EXFAIL_OUT(ret);
0700     }
0701     
0702     /* read xai from FB... */
0703     if (EXSUCCEED!=(ret = tm_forget_local(p_ub, &xai, xai.btid)))
0704     {
0705         EXFAIL_OUT(ret);
0706     }
0707     
0708 out:
0709     return ret;
0710 }
0711 
0712 /******************************************************************************/
0713 /*                         COMMAND LINE API                                   */
0714 /******************************************************************************/
0715 /**
0716  * Return list of transactions
0717  * @param p_ub
0718  * @param cd - call descriptor
0719  * @return 
0720  */
0721 expublic int tm_tpprinttrans(UBFH *p_ub, int cd)
0722 {
0723     int ret = EXSUCCEED;
0724     long revent;
0725     atmi_xa_log_list_t *tx_list;
0726     atmi_xa_log_list_t *el, *tmp;
0727     
0728     /* Get the lock! */
0729     tms_tx_hash_lock();
0730     
0731     tx_list = tms_copy_hash2list(COPY_MODE_FOREGROUND | COPY_MODE_BACKGROUND);
0732         
0733     LL_FOREACH_SAFE(tx_list,el,tmp)
0734     {
0735         
0736         /* Erase FB & setup the info there... */
0737         Bproj(p_ub, NULL); /* clear the FB! */
0738         if (EXSUCCEED!=tms_log_cpy_info_to_fb(p_ub, &(el->p_tl), EXTRUE))
0739         {
0740             EXFAIL_OUT(ret);
0741         }
0742         
0743         
0744         /* Bfprint(p_ub, stderr); */
0745         
0746         if (EXFAIL == tpsend(cd,
0747                             (char *)p_ub,
0748                             0L,
0749                             0,
0750                             &revent))
0751         {
0752             NDRX_LOG(log_error, "Send data failed [%s] %ld",
0753                                 tpstrerror(tperrno), revent);
0754             EXFAIL_OUT(ret);
0755         }
0756         else
0757         {
0758             NDRX_LOG(log_debug,"sent ok");
0759         }
0760         
0761         LL_DELETE(tx_list, el);
0762         NDRX_FREE(el);
0763     }
0764     
0765 out:
0766     /* TODO: might want to kill the list if FAIL!!!! */
0767     tms_tx_hash_unlock();
0768 
0769     return ret;
0770 }
0771 
0772 /**
0773  * Abort transaction.
0774  * @param p_ub
0775  * @param cd - call descriptor
0776  * @return 
0777  */
0778 expublic int tm_aborttrans(UBFH *p_ub)
0779 {
0780     int ret = EXSUCCEED;
0781     atmi_xa_log_t *p_tl;
0782     char tmxid[NDRX_XID_SERIAL_BUFSIZE+1];
0783     short tmrmid = EXFAIL;
0784     atmi_xa_tx_info_t xai;
0785     int locke;
0786     /* We should try to abort transaction
0787      Thus basically we need to lock the transaction on which we work on.
0788      Otherwise, we can conflict with background.
0789      */
0790     background_lock();
0791     
0792     if (EXSUCCEED!=Bget(p_ub, TMXID, 0, tmxid, 0L))
0793     {
0794         NDRX_LOG(log_error, "Failed to read TMXID: %s", 
0795                 Bstrerror(Berror));
0796         atmi_xa_set_error_msg(p_ub, TPESYSTEM, 0, "Protocol error, missing TMXID!");
0797         EXFAIL_OUT(ret);
0798     }
0799     
0800     /* optional */
0801     Bget(p_ub, TMTXRMID, 0, (char *)&tmrmid, 0L);
0802     
0803     /* Lookup for log. And then try to abort... */
0804     if (NULL==(p_tl = tms_log_get_entry(tmxid, NDRX_LOCK_WAIT_TIME, &locke)))
0805     {
0806         /* Generate error */
0807         if (locke)
0808         {
0809             atmi_xa_set_error_fmt(p_ub, TPETIME, 0, "Lock xid [%s] timed out", 
0810                     tmxid);
0811         }
0812         else
0813         {
0814             atmi_xa_set_error_fmt(p_ub, TPEMATCH, 0, "Transaction not found [%s]", 
0815                     tmxid);
0816         }
0817         EXFAIL_OUT(ret);
0818     }
0819     
0820     /* Try to abort stuff... */
0821     
0822     /* init xai for tl... */
0823     XA_TX_COPY((&xai), p_tl);
0824     
0825     NDRX_LOG(log_debug, "Got RMID: [%hd]", tmrmid);
0826     
0827     /* Switch transaction to aborting (if not already) */
0828     tms_log_stage(p_tl, XA_TX_STAGE_ABORTING, EXTRUE);
0829     
0830     if (EXSUCCEED!=(ret=tm_drive(&xai, p_tl, XA_OP_ROLLBACK, tmrmid, 0L)))
0831     {
0832         atmi_xa_set_error_fmt(p_ub, ret, NDRX_XA_ERSN_RMERR, 
0833                 "Failed to abort transaction");
0834         EXFAIL_OUT(ret);
0835     }
0836     
0837 out:
0838     background_unlock();
0839 
0840     return ret;
0841 }
0842 
0843 /**
0844  * Get transaction status
0845  * @param p_ub request buffer
0846  * @return 
0847  */
0848 expublic int tm_status(UBFH *p_ub)
0849 {
0850     int ret = EXSUCCEED;
0851     atmi_xa_log_t *p_tl = NULL;
0852     char tmxid[NDRX_XID_SERIAL_BUFSIZE+1];
0853     int locke;
0854     
0855     if (EXSUCCEED!=Bget(p_ub, TMXID, 0, tmxid, 0L))
0856     {
0857         NDRX_LOG(log_error, "Failed to read TMXID: %s", 
0858                 Bstrerror(Berror));
0859         atmi_xa_set_error_msg(p_ub, TPESYSTEM, 0, "Protocol error, missing TMXID!");
0860         EXFAIL_OUT(ret);
0861     }
0862     
0863     /* Lookup for log. And then try to abort...
0864      * in case if timed-out, we shall return different error.
0865      * maybe TPETIME.
0866      */
0867     if (NULL==(p_tl = tms_log_get_entry(tmxid, NDRX_LOCK_WAIT_TIME, &locke)))
0868     {
0869         /* Generate error */
0870         if (locke)
0871         {
0872             atmi_xa_set_error_fmt(p_ub, TPETIME, 0, "Lock xid [%s] timed out", 
0873                     tmxid);
0874         }
0875         /* detect concurrent run (for failover/singleton groups),
0876          * only for -X mode
0877          * Original E/X logic was to ignore broken logs
0878          */
0879         else if (G_tmsrv_cfg.chkdisk_time > 0 && EXFALSE!=(ret=tms_log_exists_file(tmxid)))
0880         {
0881             if (EXTRUE==ret)
0882             {
0883                 atmi_xa_set_error_fmt(p_ub, TPESYSTEM, 0, "Transaction [%s] exists on disk, "
0884                     "but not in memory - concurrent run?", tmxid);
0885             }
0886             else
0887             {
0888                 atmi_xa_set_error_fmt(p_ub, TPEOS, 0, "Transaction [%s] disk "
0889                     "log verification failure", tmxid);
0890             }
0891         }
0892         else
0893         {
0894             atmi_xa_set_error_fmt(p_ub, TPEMATCH, 0, "Transaction not found [%s]", 
0895                     tmxid);
0896             /* Verify that we had authority to respond.., as if 
0897              * we lost the lock, but other node prepared txn, but not yet synced to
0898              * the disk, we may not see the log on the disk, thus needs checkpoint.
0899              */
0900             tms_log_checkpointseq(NULL);
0901         }
0902         EXFAIL_OUT(ret);
0903     }
0904     
0905     /* Return full status of the transaction... */
0906     if (EXSUCCEED!=tms_log_cpy_info_to_fb(p_ub, p_tl, EXFALSE))
0907     {
0908         EXFAIL_OUT(ret);
0909     }
0910     
0911 out:
0912     
0913     if (NULL!=p_tl)
0914     {
0915         tms_unlock_entry(p_tl);
0916     }
0917 
0918     return ret;
0919 }
0920 
0921 /**
0922  * Commit transaction.
0923  * @param p_ub
0924  * @param cd - call descriptor
0925  * @return 
0926  */
0927 expublic int tm_committrans(UBFH *p_ub)
0928 {
0929     int ret = EXSUCCEED;
0930     atmi_xa_log_t *p_tl;
0931     char tmxid[NDRX_XID_SERIAL_BUFSIZE+1];
0932     atmi_xa_tx_info_t xai;
0933     int locke;
0934     
0935     /* We should try to commit transaction
0936      Thus basically we need to lock the transaction on which we work on.
0937      Otherwise, we can conflict with background.
0938      */
0939     background_lock();
0940     
0941     if (EXSUCCEED!=Bget(p_ub, TMXID, 0, tmxid, 0L))
0942     {
0943         NDRX_LOG(log_error, "Failed to read TMXID: %s", 
0944                 Bstrerror(Berror));
0945         atmi_xa_set_error_msg(p_ub, TPESYSTEM, 0, "Protocol error, missing TMXID!");
0946         EXFAIL_OUT(ret);
0947     }
0948     
0949     /* Lookup for log. And then try to commit... */
0950     if (NULL==(p_tl = tms_log_get_entry(tmxid, NDRX_LOCK_WAIT_TIME, &locke)))
0951     {
0952         /* Generate error */
0953         if (locke)
0954         {
0955             atmi_xa_set_error_fmt(p_ub, TPETIME, 0, "Lock xid [%s] timed out", 
0956                     tmxid);
0957         }
0958         else
0959         {
0960             atmi_xa_set_error_fmt(p_ub, TPEMATCH, 0, "Transaction not found [%s]", 
0961                     tmxid);
0962         }
0963         EXFAIL_OUT(ret);
0964     }
0965     
0966     /* Try to commit stuff, only if stage is prepared...! */
0967     if (XA_TX_STAGE_COMMITTING!=p_tl->txstage)
0968     {
0969         atmi_xa_set_error_fmt(p_ub, TPEINVAL, 0, 
0970                 "Transaction not in PREPARED stage!");
0971         /* we shall unlock the transaction here! */
0972         tms_unlock_entry(p_tl);
0973         EXFAIL_OUT(ret);
0974     }
0975     
0976     /* init xai for tl... */
0977     XA_TX_COPY((&xai), p_tl);
0978     
0979     if (EXSUCCEED!=(ret=tm_drive(&xai, p_tl, XA_OP_COMMIT, EXFAIL, 0L)))
0980     {
0981         atmi_xa_set_error_fmt(p_ub, ret, NDRX_XA_ERSN_RMCOMMITFAIL, 
0982                 "Failed to commit transaction!");
0983         ret=EXFAIL;
0984         goto out;
0985     }
0986     
0987 out:
0988     background_unlock();
0989 
0990     return ret;
0991 }
0992 
0993 /**
0994  * Return list of in doubt local transactions.
0995  * This returns info directly from RM
0996  * @param p_ub
0997  * @param cd - call descriptor
0998  * @return 
0999  */
1000 expublic int tm_recoverlocal(UBFH *p_ub, int cd)
1001 {
1002     int ret = EXSUCCEED;
1003     long revent;
1004     XID arraxid[XID_RECOVER_BLOCK_SZ];
1005     long flags = TMSTARTRSCAN;
1006     int i;
1007     char tmp[1024];
1008     size_t out_len = sizeof(tmp);
1009     
1010     while ((ret = atmi_xa_recover_entry(arraxid, XID_RECOVER_BLOCK_SZ, G_atmi_env.xa_rmid, 
1011             flags)) > 0)
1012     {       
1013         /* reset first */
1014         if (TMNOFLAGS!=flags)
1015         {
1016             flags = TMNOFLAGS;
1017         }
1018         
1019         NDRX_LOG(log_debug, "Recovered txns %d flags: %ld", ret, flags);
1020         
1021         for (i=0; i<ret; i++)
1022         {
1023             /* generate xid as base64 string? */
1024             out_len = sizeof(tmp);
1025             if (NULL==ndrx_xa_base64_encode((unsigned char *)&arraxid[i], sizeof(arraxid[i]), 
1026                     &out_len, tmp))
1027             {
1028                 NDRX_LOG(log_error, "Base64 encode failed");
1029                 EXFAIL_OUT(ret);
1030             }
1031             /* tmp[out_len] = EXEOS; */
1032             
1033             NDRX_LOG(log_debug, "Recovered xid: [%s]", tmp);
1034             
1035             if (EXSUCCEED!=Bchg(p_ub, TMXID, 0, tmp, 0))
1036             {
1037                 NDRX_LOG(log_error, "Failed to set TMXID to [%s]", tmp);
1038                 EXFAIL_OUT(ret);
1039             }
1040             
1041             if (EXFAIL == tpsend(cd,
1042                                 (char *)p_ub,
1043                                 0L,
1044                                 0,
1045                                 &revent))
1046             {
1047                 NDRX_LOG(log_error, "Send data failed [%s] %ld",
1048                                     tpstrerror(tperrno), revent);
1049                 EXFAIL_OUT(ret);
1050             }
1051             else
1052             {
1053                 NDRX_LOG(log_debug,"sent ok");
1054             }
1055         }
1056         
1057         if (ret < XID_RECOVER_BLOCK_SZ)
1058         {
1059             /* this is EOF according to the spec */
1060             break;
1061         }
1062         
1063     }
1064     
1065 out:
1066 
1067     /* close the "cursor"  - no need we scan till end..
1068     atmi_xa_recover_entry(NULL, 0, G_atmi_env.xa_rmid, TMENDRSCAN);
1069      * */
1070 
1071     return ret;
1072 }
1073 
1074 /**
1075  * Perform local operation on single xid and send results back to server.
1076  * @param p_ub UBF buffer for connection
1077  * @param cd connection descriptor
1078  * @param cmd op code
1079  * @param xid ptr XID
1080  * @param flags TMTXFLAGS flags value
1081  * @return EXSUCCEED/EXFAIL
1082  */
1083 exprivate int tm_proclocal_single(UBFH *p_ub, int cd, char cmd, XID *xid, long flags)
1084 {
1085     int ret = EXSUCCEED;
1086     char tmp[1024];
1087     size_t out_len = sizeof(tmp);
1088     long revent;
1089         
1090     atmi_xa_unset_error(p_ub);
1091     ndrx_TPunset_error();
1092     
1093     switch (cmd)
1094     {
1095         case ATMI_XA_COMMITLOCAL:
1096             ret = atmi_xa_commit_entry(xid, TMNOFLAGS);
1097             break;
1098         case ATMI_XA_ABORTLOCAL:
1099             ret = atmi_xa_rollback_entry(xid, TMNOFLAGS);
1100             break;
1101         case ATMI_XA_FORGETLOCAL:
1102             ret = atmi_xa_forget_entry(xid, TMNOFLAGS);
1103             break;
1104         default:
1105             NDRX_LOG(log_error, "Invalid Opcode: %c", cmd);
1106             EXFAIL_OUT(ret);
1107             break;
1108     }
1109     
1110     /* In case if working on non-conv mode
1111      * return the failure
1112      * and load the results
1113      */
1114     
1115     /* load the result in UBF */
1116     ndrx_TPset_error_ubf(p_ub);
1117     
1118     
1119     if (flags & TMFLAGS_NOCON)
1120     {
1121         NDRX_LOG(log_debug, "No con call: %d", ret);
1122         goto out;
1123     }
1124     
1125     ret = EXSUCCEED;
1126     
1127     ndrx_xa_base64_encode((unsigned char *)xid, sizeof(*xid), 
1128                     &out_len, tmp);
1129     /* tmp[out_len] = EXEOS; */
1130             
1131     if (EXSUCCEED!=Bchg(p_ub, TMXID, 0, tmp, 0))
1132     {
1133         NDRX_LOG(log_error, "Failed to set TMXID to [%s]", tmp);
1134         EXFAIL_OUT(ret);
1135     }
1136 
1137     if (EXFAIL == tpsend(cd,
1138                         (char *)p_ub,
1139                         0L,
1140                         0,
1141                         &revent))
1142     {
1143         NDRX_LOG(log_error, "Send data failed [%s] %ld",
1144                             tpstrerror(tperrno), revent);
1145         EXFAIL_OUT(ret);
1146     }
1147     else
1148     {
1149         NDRX_LOG(log_debug,"sent ok");
1150     }
1151     
1152 out:
1153     return ret;
1154 }
1155 
1156 /**
1157  * Process manually the local transactions.
1158  * Return the list of processed + status
1159  * @param cmd ATMI_XA_COMMITLOCAL / ATMI_XA_ABORTLOCAL / ATMI_XA_FORGETLOCAL
1160  * @param p_ub
1161  * @param cd - call descriptor
1162  * @return 
1163  */
1164 expublic int tm_proclocal(char cmd, UBFH *p_ub, int cd)
1165 {
1166     int ret = EXSUCCEED;
1167     
1168     XID one;
1169     char onestr[sizeof(XID)*2];
1170     long flags = TMSTARTRSCAN;
1171     XID arraxid[XID_RECOVER_BLOCK_SZ];
1172     int i;
1173     size_t out_len = 0;
1174     BFLDLEN len;
1175     long tmtxflags=0;
1176     
1177     /* if there is single tran, then process it, if not, then loop over */
1178     
1179     if (Bpres(p_ub, TMTXFLAGS, 0) &&
1180             EXSUCCEED!=Bget(p_ub, TMTXFLAGS, 0, (char *)&tmtxflags, 0L))
1181     {
1182         NDRX_LOG(log_error, "Failed to get TMTXFLAGS: %s", Bstrerror(Berror));
1183         EXFAIL_OUT(ret);
1184     }
1185     
1186     if (Bpres(p_ub, TMXID, 0))
1187     {
1188         NDRX_LOG(log_debug, "XID present -> process single");
1189         len = sizeof(onestr);
1190         if (EXSUCCEED!=Bget(p_ub, TMXID, 0, onestr, &len))
1191         {
1192             NDRX_LOG(log_error, "Failed to get TMXID: %s", Bstrerror(Berror));
1193             EXFAIL_OUT(ret);
1194         }
1195         
1196         ndrx_xa_base64_decode((unsigned char *)onestr, strlen(onestr), 
1197                 &out_len, (char *)&one);
1198         
1199         if (EXSUCCEED!=tm_proclocal_single(p_ub, cd, cmd, &one, tmtxflags))
1200         {
1201             NDRX_DUMP(log_error, "Failed to process local xid", &one, sizeof(one));
1202             EXFAIL_OUT(ret);
1203         }
1204             
1205     } /* we process one by one otherwise we get improper one by one process */
1206     else while ((ret = atmi_xa_recover_entry(arraxid, XID_RECOVER_BLOCK_SZ, G_atmi_env.xa_rmid, 
1207             flags)) > 0)
1208     {       
1209         /* reset first */
1210         if (TMNOFLAGS!=flags)
1211         {
1212             flags = TMNOFLAGS;
1213         }
1214         
1215         NDRX_LOG(log_debug, "Recovered txns %d flags: %ld", ret, flags);
1216         /*
1217         atmi_xa_recover_entry(NULL, 0, G_atmi_env.xa_rmid, TMENDRSCAN);
1218         */
1219         
1220         for (i=0; i<ret; i++)
1221         {   
1222             if (EXSUCCEED!=tm_proclocal_single(p_ub, cd, cmd, &arraxid[i], tmtxflags))
1223             {
1224                 NDRX_DUMP(log_error, "Failed to process local xid", &arraxid[i], 
1225                         sizeof(arraxid[i]));
1226                 EXFAIL_OUT(ret);
1227             }
1228         }
1229         
1230         if (ret < XID_RECOVER_BLOCK_SZ)
1231         {
1232             /* this is EOF according to the spec */
1233             break;
1234         }
1235         
1236     }
1237     
1238 out:
1239 
1240     return ret;
1241 }
1242 
1243 
1244 
1245 
1246 
1247 
1248 /* vim: set ts=4 sw=4 et smartindent: */