/*
#**********************************************************************#
#*                                                                    *#
#* Copyright (c) 2001 by Sun Microsystems, Inc.                       *#
#* All rights reserved.                                               *#
#*                                                                    *#
#**********************************************************************#
*/

#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <dlfcn.h>
#include <cmqc.h>

/* define return codes to userexit */
#define KXSUCCESS  0   /*  Continue with the transaction */
#define KXABORT   -1   /*  Abort transaction server      */
#define KXFAILURE  1   /*  Transaction setup failure     */

/* this is the safestored MQ connection handle */
static MQHCONN hConn = MQHC_UNUSABLE_HCONN;
/* define implementation interface */
typedef struct _KXMQImplPtrs {
	void (*mqback)(MQHCONN, PMQLONG, PMQLONG);
	void (*mqclose)(MQHCONN, PMQHOBJ, MQLONG, PMQLONG, PMQLONG);
	void (*mqcmit)(MQHCONN, PMQLONG, PMQLONG);
	void (*mqconn)(PMQCHAR, PMQHCONN, PMQLONG, PMQLONG);
	void (*mqconnx)(PMQCHAR, PMQCNO, PMQHCONN, PMQLONG, PMQLONG);
	void (*mqdisc)(PMQHCONN, PMQLONG, PMQLONG);
	void (*mqget)(MQHCONN, MQHOBJ, PMQVOID, PMQVOID, MQLONG, PMQVOID, PMQLONG, PMQLONG, PMQLONG);
	void (*mqinq)(MQHCONN, MQHOBJ, MQLONG, PMQLONG, MQLONG, PMQLONG, MQLONG, PMQCHAR, PMQLONG, PMQLONG);
	void (*mqopen)(MQHCONN, PMQVOID, MQLONG, PMQHOBJ, PMQLONG, PMQLONG);
	void (*mqput)(MQHCONN, MQHOBJ, PMQVOID, PMQVOID, MQLONG, PMQVOID, PMQLONG, PMQLONG);
	void (*mqput1)(MQHCONN, PMQVOID, PMQVOID, PMQVOID, MQLONG, PMQVOID, PMQLONG, PMQLONG);
	void (*mqset)(MQHCONN, MQHOBJ, MQLONG, PMQLONG, MQLONG, PMQLONG, MQLONG, PMQCHAR, PMQLONG, PMQLONG);
} KXMQIMPL;

/* C language implementation */
static KXMQIMPL hMQ_C_impl = { 0 };
/* COBOL language implementation */
static KXMQIMPL hMQ_Cobol_impl = { 0 };

/*
 * Helper function to prime an interface with
 * function pointers from an implementation.
 */
void hInterfaceInit(KXMQIMPL *impl, void *so)
{
	impl->mqback = (void(*)(MQHCONN, PMQLONG, PMQLONG))dlsym(so, "MQBACK");
	impl->mqclose = (void(*)(MQHCONN, PMQHOBJ, MQLONG, PMQLONG, PMQLONG))dlsym(so, "MQCLOSE");
	impl->mqcmit = (void(*)(MQHCONN, PMQLONG, PMQLONG))dlsym(so, "MQCMIT");
	impl->mqconn = (void(*)(PMQCHAR, PMQHCONN, PMQLONG, PMQLONG))dlsym(so, "MQCONN");
	impl->mqconnx = (void(*)(PMQCHAR, PMQCNO, PMQHCONN, PMQLONG, PMQLONG))dlsym(so, "MQCONNX");
	impl->mqdisc = (void(*)(PMQHCONN, PMQLONG, PMQLONG))dlsym(so, "MQDISC");
	impl->mqget = (void(*)(MQHCONN, MQHOBJ, PMQVOID, PMQVOID, MQLONG, PMQVOID, PMQLONG, PMQLONG, PMQLONG))dlsym(so, "MQGET");
	impl->mqinq = (void(*)(MQHCONN, MQHOBJ, MQLONG, PMQLONG, MQLONG, PMQLONG, MQLONG, PMQCHAR, PMQLONG, PMQLONG))dlsym(so, "MQINQ");
	impl->mqopen = (void(*)(MQHCONN, PMQVOID, MQLONG, PMQHOBJ, PMQLONG, PMQLONG))dlsym(so, "MQOPEN");
	impl->mqput = (void(*)(MQHCONN, MQHOBJ, PMQVOID, PMQVOID, MQLONG, PMQVOID, PMQLONG, PMQLONG))dlsym(so, "MQPUT");
	impl->mqput1 = (void(*)(MQHCONN, PMQVOID, PMQVOID, PMQVOID, MQLONG, PMQVOID, PMQLONG, PMQLONG))dlsym(so, "MQPUT1");
	impl->mqset = (void(*)(MQHCONN, MQHOBJ, MQLONG, PMQLONG, MQLONG, PMQLONG, MQLONG, PMQCHAR, PMQLONG, PMQLONG))dlsym(so, "MQSET");
}

/*
 * Call from user exit to allocate any MQ specifics.
 * The kixinstall activity passed on the MQ library
 * bindings (either 'Client' or 'Server') to this
 * module via the MQSERIES constant. This module has
 * therefore been compiled with that knowledge and
 * allocates the associated shared libraries as such.
 * The MQI implementations for Cobol & C are then
 * obtained and stored in a language specific interface
 * block. These pointers are used to forward an application
 * MQI call onto the correct MQ implementation.
 * This enables MTP to support Cobol and any other 
 * language that defaults to the C binding.
 */
int KXMQSALLOC()
{
	int ret = KXSUCCESS;
/* only want to do this in an MQ environment */
#ifdef MQSERIES	
	/*
	 *  Attach the MQ libraries and get address of 
	 *  intercepted functions.
	 *  The constant MQSERIES is defined by kixinstall and
	 *  can be set to either "Server" or "Client" based
	 *  upon the user selection. We use this to determine
	 *  which binding the user has requested and load
	 *  the associated language implementation.
	 */
	{
		/* kixinstall defined binding */
		char *Server="Server";	
		char *Client="Client";
		/* language implementations */
		void *soC;
		void *soCobol;
		/* implementation names */
		char *libC;
		char *libCobol;
		/* loader flags */
		int ldflags;
#ifdef RS6000
		ldflags = RTLD_LAZY | RTLD_GLOBAL | RTLD_MEMBER;
#else
		ldflags = RTLD_LAZY | RTLD_GLOBAL;
#endif
		/* use the bindings specified by kixinstall */
		if (strcmp(Server, MQSERIES) == 0) {
			/* server bindings defined */
#ifdef RS6000
			libC = "libmqm.a(libmqm.o)";
			libCobol = "libmqmcb.a(libmqmcobol.o)";
#else
			libC = "libmqm.so";
			libCobol = "libmqmcb.so";
#endif
		} else {
			/* client bindings defined */
#ifdef RS6000
			libC = "libmqic.a(mqic.o)";
			libCobol = "libmqicb.a(libmqicb.o)";
#else
			libC = "libmqic.so";
			libCobol = "libmqicb.so";
#endif			
		}
		/* open the language implementations */
		soC = dlopen(libC,  ldflags);
		soCobol = dlopen(libCobol, ldflags);
		/* populate C language implementation interface */	
		if (soC != NULL) {
			hInterfaceInit(&hMQ_C_impl, soC);
		} else {
			kxprte("Unable to open mq library %s", libC);
			ret = KXABORT;
		}
		/* populate Cobol language implementation interface */
		if (soCobol != NULL) {
			hInterfaceInit(&hMQ_Cobol_impl, soCobol);
		} else {
			kxprte("Unable to open mq library %s", libCobol);
			ret = KXABORT;
		}
	} 
#endif /* MQSERIES */
	return ret;
}

/*
 * Call from the user exit to backout this MQ UOW.
 * This is only active when "Transactional MQ" has
 * been selected. Because we have the connection
 * handle from the MQCONN, we can safely pass this
 * on to the MQBACK C language implementation.
 */
int KXMQSUNDO()
{
#ifdef MQ_SYNC_ENABLED
	/* If the handle is still valid... */
	if (hConn != MQHC_UNUSABLE_HCONN) {
		MQLONG lCompCode;
		MQLONG lReasonCode;

		hMQ_C_impl.mqback(hConn, &lCompCode, &lReasonCode);

		if (lReasonCode) {
			kxprte("MQBACK failed with CompCode [%d], ReasonCode [%d]\n",lCompCode,lReasonCode);
			return(lReasonCode);
		}
	}
#endif
	return 0;
}

/*
 * Call from the user exit to commit this MQ UOW.
 * This is only active when "Transactional MQ" has
 * been selected. Because we have the connection
 * handle from the MQCONN, we can safely pass this
 * on to the MQCMIT C language implementation.
 */
int KXMQSSAVE()
{
#ifdef MQ_SYNC_ENABLED
	/* If the handle is still valid... */
	if (hConn != MQHC_UNUSABLE_HCONN) {
		MQLONG lCompCode;
		MQLONG lReasonCode;

		hMQ_C_impl.mqcmit(hConn, &lCompCode, &lReasonCode);

		if (lReasonCode) {
			kxprte("MQCMIT failed with CompCode [%d], ReasonCode [%d]\n",lCompCode,lReasonCode);
			return(lReasonCode);
		}
	}
#endif
	return 0;
}

/*
 * Call from the user exit indicating the end of this TX.
 * Typically, the application has closed down all its MQ
 * resources and issued an MQDISC. If this is so, our local
 * connection handle has been reset. If the handle is still
 * valid, then MQ is directed to disconnect the handle.
 */
int KXMQSETRN()
{
#ifdef MQSERIES
	/* 
	 * If the handle is still valid, then
	 * this TX has ended without disconnecting
	 * the connection. Take care of disconnecting here.
	 */
	if (hConn != MQHC_UNUSABLE_HCONN) {
		MQLONG lCompCode;
		MQLONG lReasonCode;

		MQDISC(&hConn, &lCompCode, &lReasonCode);

		if (lReasonCode) {
			kxprte("MQDISC failed with CompCode [%d], ReasonCode [%d]\n",lCompCode,lReasonCode);
			return(lReasonCode);
		}
	}
#endif
	return 0;
}

/*
 *    Intercept these MQ entry points so we can manage
 *    the MQ connection. 
 *    MQ has a specific interface for the COBOL language, so
 *    we must attempt to determine which language the caller
 *    is using. The approach here is to check the passed connection
 *    handle. If it is not the same as the handle we safestored
 *    following the MQCONN, then we assume the handle is actually a
 *    reference to the handle and this caller is COBOL. The basis for
 *    this is that the 'C' binding uses pass-by-value, so the handle
 *    is passed to several functions. COBOL always uses pass-by-reference,
 *    so the handle is never passed directly. Functions MQCONN & MQDISC
 *    have the same semantics for both C and COBOL, so we do not need
 *    to differentiate in these cases.
 *    Note that only a single MQ queue manager is supported. This is
 *    enforced in an XA environment (during XA startup, not here), but
 *    not in a non-XA environment. A second call to MQCONN/MQCONNX will
 *    destroy our copy of the existing connection handle. If this must
 *    be handled, additional checking is required BEFORE the connect call is
 *    forwarded on to MQ.
 */
#ifdef MQSERIES

/*********************************************************************/
/*  MQBACK Function -- Back Out Changes                              */
/*********************************************************************/
void MQBACK (
   MQHCONN  Hconn,      /* Connection handle */
   PMQLONG  pCompCode,  /* Completion code */
   PMQLONG  pReason)    /* Reason code qualifying CompCode */
{
	/* This is an illegal call in an MTP environment */
	*pCompCode = MQCC_FAILED;
	*pReason = MQRC_ENVIRONMENT_ERROR;
}

/*********************************************************************/
/*  MQBEGIN Function -- Begin Unit of Work                           */
/*********************************************************************/
void MQBEGIN (
   MQHCONN  Hconn,          /* Connection handle */
   PMQVOID  pBeginOptions,  /* Options that control the action of
                               MQBEGIN */
   PMQLONG  pCompCode,      /* Completion code */
   PMQLONG  pReason)        /* Reason code qualifying CompCode */
{
	/* This is an illegal call in an MTP environment */
	*pCompCode = MQCC_FAILED;
	*pReason = MQRC_ENVIRONMENT_ERROR;
}

/*********************************************************************/
/*  MQCLOSE Function -- Close Object                                 */
/*********************************************************************/
void MQCLOSE (
   MQHCONN  Hconn,      /* Connection handle */
   PMQHOBJ  pHobj,      /* Object handle */
   MQLONG   Options,    /* Options that control the action of MQCLOSE */
   PMQLONG  pCompCode,  /* Completion code */
   PMQLONG  pReason)    /* Reason code qualifying CompCode */
{
	/* Cobol binding is default */
	KXMQIMPL* impl = &hMQ_Cobol_impl;
	/* If the handle is passed-by-value... */
	if (Hconn == hConn) {
		/* switch to C binding */
		impl = &hMQ_C_impl;
	} 
	/* invoke language binding */
	impl->mqclose(Hconn, pHobj, Options, pCompCode, pReason);
}

/*********************************************************************/
/*  MQCMIT Function -- Commit Changes                                */
/*********************************************************************/
void MQCMIT (
   MQHCONN  Hconn,      /* Connection handle */
   PMQLONG  pCompCode,  /* Completion code */
   PMQLONG  pReason)    /* Reason code qualifying CompCode */
{
	/* This is an illegal call in an MTP environment */
	*pCompCode = MQCC_FAILED;
	*pReason = MQRC_ENVIRONMENT_ERROR;
}

/*********************************************************************/
/*  MQCONN Function -- Connect Queue Manager                         */
/*********************************************************************/
void MQCONN (
   PMQCHAR   pQMgrName,  /* Name of queue manager */
   PMQHCONN  pHconn,     /* Connection handle */
   PMQLONG   pCompCode,  /* Completion code */
   PMQLONG   pReason)    /* Reason code qualifying CompCode */ 
{
	/* Language binding not required on MQCONN */
	hMQ_C_impl.mqconn(pQMgrName, pHconn, pCompCode, pReason);
	if (*pCompCode == MQCC_OK) {
		/* keep a local copy of the handle */
		hConn = *pHconn;
	}
}

/*********************************************************************/
/*  MQCONNX Function -- Connect Queue Manager (Extended)             */
/*********************************************************************/
void MQCONNX (
   PMQCHAR   pQMgrName,     /* Name of queue manager */
   PMQCNO    pConnectOpts,  /* Options that control the action of
                               MQCONNX */
   PMQHCONN  pHconn,        /* Connection handle */
   PMQLONG   pCompCode,     /* Completion code */
   PMQLONG   pReason)       /* Reason code qualifying CompCode */
{
	/* Language binding not required on MQCONNX */
	hMQ_C_impl.mqconnx(pQMgrName, pConnectOpts, pHconn, pCompCode, pReason);
	if (*pCompCode == MQCC_OK) {
		/* keep a local copy of the handle */
		hConn = *pHconn;
	}

}
/*********************************************************************/
/*  MQDISC Function -- Disconnect Queue Manager                      */
/*********************************************************************/
void MQDISC (
   PMQHCONN  pHconn,     /* Connection handle */
   PMQLONG   pCompCode,  /* Completion code */
   PMQLONG   pReason)    /* Reason code qualifying CompCode */
{
	/* Language binding not required on MQDISC */
	hMQ_C_impl.mqdisc(pHconn, pCompCode, pReason);
	if (*pCompCode == MQCC_OK) {
		/* update our local copy of the handle */
		hConn = *pHconn;
	}
}

/*********************************************************************/
/*  MQGET Function -- Get Message                                    */
/*********************************************************************/
void MQGET (
   MQHCONN  Hconn,         /* Connection handle */
   MQHOBJ   Hobj,          /* Object handle */
   PMQVOID  pMsgDesc,      /* Message descriptor */
   PMQVOID  pGetMsgOpts,   /* Options that control the action of
                              MQGET */
   MQLONG   BufferLength,  /* Length in bytes of the Buffer area */
   PMQVOID  pBuffer,       /* Area to contain the message data */
   PMQLONG  pDataLength,   /* Length of the message */
   PMQLONG  pCompCode,     /* Completion code */
   PMQLONG  pReason)       /* Reason code qualifying CompCode */
{
	/* Cobol binding is default */
	KXMQIMPL* impl = &hMQ_Cobol_impl;
	/* If the handle is passed-by-value... */
	if (Hconn == hConn) {
		/* switch to C binding */
		impl = &hMQ_C_impl;
	} 
	/* invoke language binding */
	impl->mqget(Hconn, Hobj, pMsgDesc, pGetMsgOpts, 
		BufferLength, pBuffer, pDataLength, pCompCode, pReason);
}

/*********************************************************************/
/*  MQINQ Function -- Inquire Object Attributes                      */
/*********************************************************************/
void MQINQ (
   MQHCONN  Hconn,           /* Connection handle */
   MQHOBJ   Hobj,            /* Object handle */
   MQLONG   SelectorCount,   /* Count of selectors */
   PMQLONG  pSelectors,      /* Array of attribute selectors */
   MQLONG   IntAttrCount,    /* Count of integer attributes */
   PMQLONG  pIntAttrs,       /* Array of integer attributes */
   MQLONG   CharAttrLength,  /* Length of character attributes buffer */
   PMQCHAR  pCharAttrs,      /* Character attributes */
   PMQLONG  pCompCode,       /* Completion code */
   PMQLONG  pReason)         /* Reason code qualifying CompCode */
{
	/* Cobol binding is default */
	KXMQIMPL* impl = &hMQ_Cobol_impl;
	/* If the handle is passed-by-value... */
	if (Hconn == hConn) {
		/* switch to C binding */
		impl = &hMQ_C_impl;
	} 
	/* invoke language binding */
	impl->mqinq(Hconn, Hobj, SelectorCount, pSelectors, 
		IntAttrCount, pIntAttrs, CharAttrLength, pCharAttrs, 
		pCompCode, pReason);
}

/*********************************************************************/
/*  MQOPEN Function -- Open Object                                   */
/*********************************************************************/
void MQOPEN (
   MQHCONN  Hconn,      /* Connection handle */
   PMQVOID  pObjDesc,   /* Object descriptor */
   MQLONG   Options,    /* Options that control the action of MQOPEN */
   PMQHOBJ  pHobj,      /* Object handle */
   PMQLONG  pCompCode,  /* Completion code */
   PMQLONG  pReason)    /* Reason code qualifying CompCode */
{
	/* Cobol binding is default */
	KXMQIMPL* impl = &hMQ_Cobol_impl;
	/* If the handle is passed-by-value... */
	if (Hconn == hConn) {
		/* switch to C binding */
		impl = &hMQ_C_impl;
	} 
	/* invoke language binding */
	impl->mqopen(Hconn, pObjDesc, Options, pHobj, pCompCode, pReason);
}

/*********************************************************************/
/*  MQPUT Function -- Put Message                                    */
/*********************************************************************/
void MQPUT (
   MQHCONN  Hconn,         /* Connection handle */
   MQHOBJ   Hobj,          /* Object handle */
   PMQVOID  pMsgDesc,      /* Message descriptor */
   PMQVOID  pPutMsgOpts,   /* Options that control the action of
                              MQPUT */
   MQLONG   BufferLength,  /* Length of the message in Buffer */
   PMQVOID  pBuffer,       /* Message data */
   PMQLONG  pCompCode,     /* Completion code */
   PMQLONG  pReason)       /* Reason code qualifying CompCode */
{
	/* Cobol binding is default */
	KXMQIMPL* impl = &hMQ_Cobol_impl;
	/* If the handle is passed-by-value... */
	if (Hconn == hConn) {
		/* switch to C binding */
		impl = &hMQ_C_impl;
	} 
	/* invoke language binding */
	impl->mqput(Hconn, Hobj, pMsgDesc, pPutMsgOpts, 
		BufferLength, pBuffer, pCompCode, pReason);
}

/*********************************************************************/
/*  MQPUT1 Function -- Put One Message                               */
/*********************************************************************/
void MQPUT1 (
   MQHCONN  Hconn,         /* Connection handle */
   PMQVOID  pObjDesc,      /* Object descriptor */
   PMQVOID  pMsgDesc,      /* Message descriptor */
   PMQVOID  pPutMsgOpts,   /* Options that control the action of
                              MQPUT1 */
   MQLONG   BufferLength,  /* Length of the message in Buffer */
   PMQVOID  pBuffer,       /* Message data */
   PMQLONG  pCompCode,     /* Completion code */
   PMQLONG  pReason)       /* Reason code qualifying CompCode */
{
	/* Cobol binding is default */
	KXMQIMPL* impl = &hMQ_Cobol_impl;
	/* If the handle is passed-by-value... */
	if (Hconn == hConn) {
		/* switch to C binding */
		impl = &hMQ_C_impl;
	} 
	/* invoke language binding */
	impl->mqput1(Hconn, pObjDesc, pMsgDesc, pPutMsgOpts, 
		BufferLength, pBuffer, pCompCode, pReason);
}

/*********************************************************************/
/*  MQSET Function -- Set Object Attributes                          */
/*********************************************************************/
void MQSET (
   MQHCONN  Hconn,           /* Connection handle */
   MQHOBJ   Hobj,            /* Object handle */
   MQLONG   SelectorCount,   /* Count of selectors */
   PMQLONG  pSelectors,      /* Array of attribute selectors */
   MQLONG   IntAttrCount,    /* Count of integer attributes */
   PMQLONG  pIntAttrs,       /* Array of integer attributes */
   MQLONG   CharAttrLength,  /* Length of character attributes buffer */
   PMQCHAR  pCharAttrs,      /* Character attributes */
   PMQLONG  pCompCode,       /* Completion code */
   PMQLONG  pReason)         /* Reason code qualifying CompCode */
{
	/* Cobol binding is default */
	KXMQIMPL* impl = &hMQ_Cobol_impl;
	/* If the handle is passed-by-value... */
	if (Hconn == hConn) {
		/* switch to C binding */
		impl = &hMQ_C_impl;
	} 
	/* invoke language binding */
	impl->mqset(Hconn, Hobj, SelectorCount, pSelectors, 
		IntAttrCount, pIntAttrs, CharAttrLength, pCharAttrs, 
		pCompCode, pReason);
}
#endif /* MQSERIES */
