MultipleRM 프로그램 예제

본 장에서는 TCS에서 MultipleRM 프로그램 예제와 환경설정, 컴파일을 위한 Makefile의 예제에 대해서 설명한다.

1. Oracle과 Tibero 사용

서버 라이브러리를 이용하여 TCS에서 Oracle과 Tibero를 사용할 수 있다. 본 절에서는 Oracle과 Tibero를 사용하는 프로그램의 예제와 환경설정, 컴파일을 위한 Makefile의 예제를 설명한다.

1.1. 프로그램 예제

다음은 Oracle과 Tibero를 함께 사용하는 TCS 예제 프로그램이다. Embedded SQL을 이용하여 서버를 작성하고 각 데이터베이스 벤더에서 제공하는 프리 컴파일러로 컴파일하여 구성할 수 있는 프로그램 예제이다.

서비스 수행

다음은 서비스를 수행하는 프로그램의 소스이다.

#include <stdio.h>
#include <usrinc/atmi.h>

extern int oracle_call(TPSVCINFO *msg);
extern int tibero_call(TPSVCINFO *msg);

MDB_SVC1(TPSVCINFO *msg)
{
    int ret;
    int fail = 0;

    ret = oracle_call(msg);
    if (ret < 0) {
        fail = 1;
    }

    ret = tibero_call(msg);
    if (ret < 0) {
        fail = 1;
    }

    if ( fail ) {
        tpreturn(TPFAIL,0,(char *)msg->data, msg->len,0);
    }

    tpreturn(TPSUCCESS,0,(char *)msg->data, msg->len,0);
}
Oracle 호출 함수

다음은 Oracle을 호출하는 함수를 사용한 프로그램의 예제이다.

#include <stdio.h>
#include <usrinc/atmi.h>
#include <usrinc/tx.h>

EXEC SQL include sqlca.h;

EXEC SQL begin declare section;
    char h_addr[100];
EXEC SQL end declare section;

int oracle_call(TPSVCINFO *msg)
{
    int     i;

    strcpy(h_addr, msg->data);

    EXEC SQL INSERT
        INTO account( account_id, address, phone )
        VALUES ( tmax_seq.nextval,:h_addr, '007-1234' );

    if ( sqlca.sqlcode != 0 ){
        printf( "insert failed sqlcode = %d\n",sqlca.sqlcode );
        tpreturn( TPFAIL, -1, NULL, 0, 0 );
    }

    printf("ORACLE insert Success\n");

    return 0;
}
Tibero 호출 함수

다음은 Tibero를 호출하는 함수를 사용한 프로그램의 예제이다.

#include <stdio.h>
#include <string.h>
#include <usrinc/atmi.h>

EXEC SQL include sqlca.h;

EXEC SQL begin declare section;
    char h_addr[100];
EXEC SQL end declare section;

int tibero_call(TPSVCINFO *msg)
{
    strcpy(h_addr, msg->data);

    EXEC SQL INSERT
        INTO account( account_id, address, phone )
        VALUES ( tmax_seq.nextval,:h_addr, '007-1234' );

    if ( sqlca.sqlcode != 0 ){
        printf( "tibero insert failed sqlcode = %d\n",sqlca.sqlcode );
        tpreturn( TPFAIL, -1, NULL, 0, 0 );
    }

    printf("TIBERO insert Success\n");

    return 0;
}

1.2. 환경설정

Oracle과 Tibero를 사용하는 MultipleRM의 환경설정 예제를 설명한다.

*DOMAIN
domain        SHMKEY   = 81522, MAXUSER = 10000,
              MINCLH   = 1, MAXCLH = 1,
              TPORTNO  = 11500,  BLOCKTIME = 30,
              MAXSACALL = 1024, MAXCACALL = 1024,
              MAXMTMAX = 3, MAXSTMAX = 10

*NODE
node1         TMAXDIR  = "/home/tmax",
              APPDIR   = "/home/tmax/appbin",
              PATHDIR  = "/home/tmax/path",
              TLOGDIR  = "/home/tmax/log/tlog",
              ULOGDIR  = "/home/tmax/log/ulog",
              SLOGDIR  = "/home/tmax/log/slog"

*SVRGROUP
svg_s1        NODENAME = node1,
              DBNAME = ORACLE,
              OPENINFO ="Oracle_Xa+SqlNet=TMAX+Acc=P/scott/tiger+SesTm=60",
              TMSNAME = tms_ora,
              SVGTYPE = STMAX

svg_s2        NODENAME = node1,
              DBNAME = TIBERO,
              OPENINFO ="TIBERO_XA:user=scott,pwd=tiger, sestm=60,db=TMAX",
              TMSNAME = tms_tbr,
              SVGTYPE = STMAX

1.3. 컴파일

본 절에서는 Oracle과 Tibero를 사용하는 MultipleRM의 컴파일 예제를 설명한다.

프로그램을 빌드할 때 _tmax_xasw_init() 함수가 정의된 라이브러리(예: liboras.so)가 링크되어 있는 경우 RM 파일"라이브러리에서 목록 추출하는 방법"의 동작을 따른다. 그렇지 않은 경우에는 "설정 파일에 정의된 내용을 따르는 방법"의 동작을 따른다. Makefile 내의 LIBS(TMAX라이브러리)에 반드시 -lnodb를 포함해야 한다.

운영체제에 따라 Makefile 내용은 다를 수 있다.

다음은 32bit Linux에서 MultipleRM 서버 프로그램을 컴파일하기 위한 Makefile의 예제이다.

# Oracle Env
ORALIBDIR = /home/OraHome1/lib/
ORALIB = -lclntsh `cat /home/OraHome1/lib/ldflags`   `cat /home/OraHome1/lib/sysliblist` -ldl -lm

# Tibero Env
TBRLIB = -ltbxa -ltbertl
TBRLIBDIR = $(TB_HOME)/client/lib
TBRINCDIR = $(TB_HOME)/client/include

$ Server build
TARGET  = $(COMP_TARGET)
APOBJS  = $(TARGET).o
AP_ORA_OBJS = $(TARGET)_ora.o
AP_TBR_OBJS = $(TARGET)_tbr.o
NSDLOBJ = $(TMAXDIR)/lib/sdl.o

LIBS    = -lnsl -lsvr -lnodb

OBJS    = $(AP_ORA_OBJS) $(AP_TBR_OBJS) $(SVCTOBJ) $(APOBJS)
SVCTOBJ = $(TARGET)_svctab.o

CFLAGS  =  -O -I$(TMAXDIR)

APPDIR  = $(TMAXDIR)/appbin
SVCTDIR = $(TMAXDIR)/svct
TMAXLIBDIR  = $(TMAXDIR)/lib

#
.SUFFIXES : .c

.c.o:
    echo $(OBJS)
    $(CC) $(CFLAGS) -c $<

#
# server compile
#
all: $(TARGET)


$(TARGET):$(OBJS)
    echo $(OBJS)
    $(CC) $(CFLAGS) -L$(TMAXLIBDIR) -o $(TARGET) -L$(TBRLIBDIR) -L$(ORALIBDIR) $(ORALIB) $(TBRLIB) $(OBJS) $(LIBS) $(NSDLOBJ)
    mv $(TARGET) $(APPDIR)/.
    rm -f $(OBJS)

$(AP_ORA_OBJS):
    echo $(OBJS)
    proc iname=$(TARGET)_ora.pc include=$(TMAXDIR)
    $(CC) $(CFLAGS) -c $(TARGET)_ora.c

$(AP_TBR_OBJS):
    echo $(OBJS)
    tbpc iname=$(TARGET)_tbr.tbc include=$(TMAXDIR)
    $(CC) $(CFLAGS) -I$(TBRINCDIR) -c $(TARGET)_tbr.c

$(APOBJS):
    echo $(OBJS)
    $(CC) $(CFLAGS) -c $(TARGET).c

$(SVCTOBJ):
    echo $(OBJS)
    cp -f $(SVCTDIR)/$(TARGET)_svctab.c .
    touch ./$(TARGET)_svctab.c
    $(CC) $(CFLAGS) -c ./$(TARGET)_svctab.c

#
clean:
    -rm -f *.o core $(TARGET) $(TARGET).lis

2. Oracle과 Oracle 사용

서버 라이브러리를 이용하여 TCS에서 Oracle과 Oracle을 사용할 수 있다. 본 절에서는 Oracle과 Oracle을 사용하는 프로그램의 예제와 환경설정, 컴파일을 위한 Makefile의 예제를 설명한다.

2.1. 프로그램 예제

다음은 2개의 RM이 모두 Oracle이지만 인스턴스가 다른 경우에 한 서버에서 처리하는 예제이다.

같은 Oracle 클라이언트 라이브러리를 사용하기 때문에 사용자 코드에서 쿼리를 수행할 경우 어떤 인스턴트를 수행할지는 사용자가 지정해야 한다. 이를 위해서 Oracle에서는 xa_open의 경우 사용자 코드에서 xaoSvcCtx, xaoEnv 등의 OCI 함수를 이용하여 해당하는 인스턴트 커넥션을 얻어와서 지정할 수 있다.

서비스 수행

다음은 서비스를 수행하는 프로그램의 소스이다.

#include <stdio.h>
#include <usrinc/atmi.h>

extern int oracle_call(TPSVCINFO *msg);

MDB_SVC2(TPSVCINFO *msg)
{
    int ret;
    int fail = 0;

    ret = oracle_call(msg);
    if (ret < 0) {
        fail = 1;
    }

    if ( fail ) {
        tpreturn(TPFAIL,0,(char *)msg->data, msg->len,0);
    }

    tpreturn(TPSUCCESS,0,(char *)msg->data, msg->len,0);
}
Oracle 호출 함수

다음은 Oracle을 호출하는 함수를 사용하는 프로그램의 예제이다.

#include <stdio.h>
#include <usrinc/atmi.h>
#include <usrinc/tx.h>
#include <oci.h>

static void checkerr(errhp, status)
OCIError *errhp;
sword status;
{
    text errbuf[512];
    sb4 errcode;

    switch (status)
    {
        case OCI_SUCCESS:
            break;
        case OCI_SUCCESS_WITH_INFO:
            break;
        case OCI_NEED_DATA:
            break;
        case OCI_NO_DATA:
            break;
        case OCI_ERROR:  /* get the error back and display on the screen */
            (void) OCIErrorGet ((dvoid *) errhp, (ub4) 1, (text *) NULL, &errcode,
                                errbuf, (ub4) sizeof(errbuf), (ub4) OCI_HTYPE_ERROR);
            (void) printf("Error - %s\n", errbuf);
            break;
        case OCI_INVALID_HANDLE:
            break;
        case OCI_STILL_EXECUTING:
            break;
        case OCI_CONTINUE:
            break;
        default:
            break;
    }
}

int oracle_call(TPSVCINFO *msg)
{
    int     i;
    OCIEnv *envhp;
    OCIServer *srvhp;
    OCIError *errhp;
    OCISvcCtx *svchp;
    OCISession *usrhp;
    OCIStmt *stmthp;
    dvoid *tmp;
    char *sql = {"INSERT "
        "INTO hahehiho_account( account_id, address, phone ) "
        "VALUES ( skt_seq.nextval,'asdf', '007-1234' )"};

    char *sql2 = {"INSERT "
        "INTO hahehiho_account( account_id, address, phone ) "
        "VALUES ( skt_seq.nextval,'fdsa', '700-1234' )"};

    svchp = xaoSvcCtx("A");
    envhp = xaoEnv("A");
    printf("oracle call A octxt[%d]\n", svchp);

    OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &errhp, (ub4) OCI_HTYPE_ERROR,
                    52, (dvoid **) &tmp);

    OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &stmthp, (ub4) OCI_HTYPE_STMT,
                    50, (dvoid **) &tmp);

    checkerr(errhp, OCIStmtPrepare(stmthp, errhp,(text *)sql, (ub4)strlen(sql),
            (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT));

    checkerr(errhp, OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4) 0,
            (OCISnapshot *) NULL, (OCISnapshot *) NULL,
            (ub4) OCI_DEFAULT));

    svchp = xaoSvcCtx("B");
    envhp = xaoEnv("B");
    printf("oracle call B octxt[%d]\n", svchp);

    OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &errhp, (ub4) OCI_HTYPE_ERROR,
                           52, (dvoid **) &tmp);

    OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &stmthp,
               (ub4) OCI_HTYPE_STMT, 50, (dvoid **) &tmp);

    checkerr(errhp, OCIStmtPrepare(stmthp, errhp,(text *)sql2, (ub4)strlen(sql2),
            (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT));

    checkerr(errhp, OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4) 0,
            (OCISnapshot *) NULL, (OCISnapshot *) NULL,
            (ub4) OCI_DEFAULT));

    return 0;
}

2.2. 환경설정 및 컴파일

Oracle과 Oracle을 사용하는 MultipleRM의 환경설정 및 컴파일 예제를 설명한다.

환경설정

다음은 MultipleRM 서버의 환경설정 파일의 예제이다.

*DOMAIN
domain        SHMKEY   = 81522, MAXUSER = 10000,
              MINCLH   = 1, MAXCLH = 1,
              TPORTNO  = 11500,  BLOCKTIME = 30,
              MAXSACALL = 1024, MAXCACALL = 1024,
              MAXMTMAX = 3, MAXSTMAX = 10

*NODE
node1         TMAXDIR  = "/home/tmax",
              APPDIR   = "/home/tmax/appbin",
              PATHDIR  = "/home/tmax/path",
              TLOGDIR  = "/home/tmax/log/tlog",
              ULOGDIR  = "/home/tmax/log/ulog",
              SLOGDIR  = "/home/tmax/log/slog"

*SVRGROUP
svg_s1        NODENAME = node1,
              DBNAME = ORACLE,
              OPENINFO ="Oracle_Xa+SqlNet=TMAX1+Acc=P/scott/tiger+SesTm=60+DB=A",
              TMSNAME = tms_ora,
              SVGTYPE = STMAX,
              RMID = 0

svg_s2        NODENAME = node1,
              DBNAME = ORACLE,
              OPENINFO ="Oracle_Xa+SqlNet=TMAX2+Acc=P/scott/tiger+SesTm=60+DB=B",
              TMSNAME = tms_ora,
              SVGTYPE = STMAX,
              RMID = 1

svgm1         NODENAME = node1,
              SVGLIST = "svg_s2,svg_s1",
              SVGTYPE = MTMAX

*SERVER
sample SVGNAME = svgm1, LOGLVL = DEBUG4

*SERVICE
MDB_SVC2 SVRNAME = sample
Makefile

Makefile의 TMAXLIBS에 반드시 -lnodb를 포함해야 한다. 다음은 32bit Linux에서 MultipleRM 서버 프로그램을 컴파일하기 위한 Makefile의 예이다.

# Oracle Env
ORALIBDIR = /home/OraHome1/lib/
ORALIB = -lclntsh `cat /home/OraHome1/lib/ldflags`   `cat /home/OraHome1/lib/sysliblist` -ldl -lm

LIBS    = -lnsl -lsvrmrm

OBJS    = $(AP_ORA_OBJS) $(AP_TBR_OBJS) $(SVCTOBJ) $(APOBJS)
SVCTOBJ = $(TARGET)_svctab.o

CFLAGS  =  -O -I$(TMAXDIR)

APPDIR  = $(TMAXDIR)/appbin
SVCTDIR = $(TMAXDIR)/svctTARGET  = $(COMP_TARGET)
APOBJS  = $(TARGET).o
AP_ORA_OBJS = $(TARGET)_ora.o
NSDLOBJ = $(TMAXDIR)/lib/sdl.o

LIBS    = -lnsl -lsvr -lnodb

OBJS    = $(AP_ORA_OBJS) $(SVCTOBJ) $(APOBJS)
SVCTOBJ = $(TARGET)_svctab.o

CFLAGS  =  -O -I$(TMAXDIR) -I$(ORACLE_HOME)/include -I$(ORACLE_HOME)/rdbms/demo -I$(ORACLE_HOME)/rdbms/public

APPDIR  = $(TMAXDIR)/appbin
SVCTDIR = $(TMAXDIR)/svct
TMAXLIBDIR  = $(TMAXDIR)/lib

#
.SUFFIXES : .c

.c.o:
    echo $(OBJS)
    $(CC) $(CFLAGS) -c $<

#
# server compile
#
all: $(TARGET)


$(TARGET):$(OBJS)
    $(CC) $(CFLAGS) -L$(TMAXLIBDIR) -o $(TARGET) -L$(ORALIBDIR) $(ORALIB)  $(OBJS) $(LIBS) $(NSDLOBJ)
    mv $(TARGET) $(APPDIR)/.
    rm -f $(OBJS)

$(AP_ORA_OBJS):
    $(CC) $(CFLAGS) -c $(TARGET)_ora.c

$(APOBJS):
    $(CC) $(CFLAGS) -c $(TARGET).c

$(SVCTOBJ):
    echo $(OBJS)
    cp -f $(SVCTDIR)/$(TARGET)_svctab.c .
    touch ./$(TARGET)_svctab.c
    $(CC) $(CFLAGS) -c ./$(TARGET)_svctab.c

#
clean:
    -rm -f *.o core $(TARGET) $(TARGET).lis

운영체제에 따라 Makefile 내용은 다를 수 있다.

3. Oracle과 DB2 사용

서버 라이브러리를 이용하여 TCS에서 Oracle과 DB2를 사용할 수 있다. 본 절에서는 Oracle과 DB2를 사용하는 프로그램의 예제와 환경설정, 컴파일을 위한 Makefile의 예제를 설명한다.

3.1. 프로그램 예제

다음은 Oracle과 DB2를 함께 사용하는 TCS 예제 프로그램이다. Embedded SQL을 이용하여 서버를 작성하고 각 데이터베이스 벤더에서 제공하는 프리 컴파일러로 컴파일하여 구성할 수 있는 프로그램 예제이다.

서비스 수행

다음은 서비스를 수행하는 프로그램의 소스이다.

#include <stdio.h>
#include <ctype.h>
#include <usrinc/atmi.h>
#include <usrinc/tx.h>


extern int SVC2301TX_1_oracle(TPSVCINFO *msg);
extern int SVC2301TX_1_db2(TPSVCINFO *msg);
extern int SVC2301TX_2_oracle(TPSVCINFO *msg);
extern int SVC2301TX_2_db2(TPSVCINFO *msg);
extern int SVC2301TX_3_oracle(TPSVCINFO *msg);
extern int SVC2301TX_3_db2(TPSVCINFO *msg);
extern int SVC2301TX_4_oracle(TPSVCINFO *msg);
extern int SVC2301TX_4_db2(TPSVCINFO *msg);

SVC2301TX_1( TPSVCINFO *msg )
{
    int ret;
    int fail = 0;
    char *rcvbuf;

    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
        if(rcvbuf==NULL) {
                printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
                tpreturn(TPFAIL, -1, NULL, 0, 0);
        }

    ret = SVC2301TX_1_oracle(msg);
    if (ret < 0) {
        fail = 1;
    }

    ret = SVC2301TX_1_db2(msg);
    if (ret < 0) {
        fail = 1;
    }

    if ( fail ) {
        printf("SVC2301TX_1  FAIL\n");
        tpreturn(TPFAIL,-1,NULL, 0,0);
    }

    strcpy(rcvbuf, "Multiple RM Insert Success");
    tpreturn( TPSUCCESS, 0, rcvbuf, strlen(rcvbuf), 0 );
}

SVC2301TX_2( TPSVCINFO *msg )
{
    int ret;
    int fail = 0;
    char *rcvbuf;

    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    f(rcvbuf==NULL) {
        printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
        tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    ret = SVC2301TX_2_oracle(msg);
    if (ret < 0) {
        fail = 1;
    }

    ret = SVC2301TX_2_db2(msg);
    if (ret < 0) {
        fail = 1;
    }
    if ( fail ) {
        printf("SVC2301TX_2  FAIL\n");
        tpreturn(TPFAIL,-1,NULL, 0,0);
    }
    strcpy(rcvbuf, "Multiple RM Update Success");
    tpreturn( TPSUCCESS, 0, rcvbuf, strlen(rcvbuf), 0 );
}

SVC2301TX_3( TPSVCINFO *msg )
{
    int ret;
    int fail = 0;
    char *rcvbuf;

    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
        printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
        tpreturn(TPFAIL, -1, NULL, 0, 0);
     }


    ret = SVC2301TX_3_oracle(msg);
    if (ret < 0) {
        fail = 1;
    }

    ret = SVC2301TX_3_db2(msg);
    if (ret < 0) {
        fail = 1;
    }

    if ( fail ) {
        printf("SVC2301TX_3  FAIL\n");
        tpreturn(TPFAIL,-1,NULL, 0,0);
    }
    strcpy(rcvbuf, "Multiple RM Delete Success");
    tpreturn( TPSUCCESS, 0, rcvbuf, strlen(rcvbuf), 0 );
}

SVC2301TX_4( TPSVCINFO *msg )
{
    int ret;
    int fail = 0;
    char *rcvbuf;

    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
        printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
        tpreturn(TPFAIL, -1, NULL, 0, 0);
    }


    ret = SVC2301TX_4_oracle(msg);
    if (ret < 0) {
        fail = 1;
    }

    ret = SVC2301TX_4_db2(msg);
    if (ret < 0) {
        fail = 1;
    }

    if ( fail ) {
        printf("SVC2301TX_4  FAIL\n");
        tpreturn(TPFAIL,-1,NULL, 0,0);
    }

    strcpy(rcvbuf, "Multiple RM Select Success");
    tpreturn( TPSUCCESS, 0, rcvbuf, strlen(rcvbuf), 0 );
}
Oracle 호출 함수

다음은 Oracle을 호출하는 함수를 사용한 프로그램의 예제이다.

#include <stdio.h>
#include <ctype.h>
#include <usrinc/atmi.h>
#include <usrinc/tx.h>

EXEC SQL INCLUDE SQLCA.H;

EXEC SQL BEGIN DECLARE SECTION;
    int  h_empno;
    char h_ename[MAXLEN];
    char h_job[MAXLEN];
    char h_date[MAXLEN];
    float h_sal;
    int  h_count;
    char h_xid[100];
EXEC SQL END DECLARE SECTION;

SVC2301TX_1_oracle( TPSVCINFO *msg )
{
    str  sndbuf;
    char *rcvbuf;
    char tmp[1024];
    TXINFO info;

    sndbuf = (str)msg->data;
    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
        printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
        tpreturn(TPFAIL, -1, NULL, 0, 0);
     }

    h_empno = h_sal = 0;
    memset( h_ename, 0x00, sizeof( h_ename ) );
    memset( h_job, 0x00, sizeof( h_job ) );
    memset( h_date, 0x00, sizeof( h_date ) );
    memset( tmp , 0x00, sizeof( tmp  ) );
    memset( h_xid , 0x00, sizeof( h_xid  ) );

    h_empno = sndbuf->empno;
    h_sal   = sndbuf->sal;
    strcpy( h_ename, sndbuf->ename );
    strcpy( h_job  , sndbuf->job   );
    strcpy( h_date , sndbuf->date  );

    if (tx_info(&info)==1) {
            sprintf(h_xid, "%x %x %x %x - %x %x %x %x",
                (unsigned char)info.xid.data[0],
                (unsigned char)info.xid.data[1],
                (unsigned char)info.xid.data[2],
                (unsigned char)info.xid.data[3],
                (unsigned char)info.xid.data[4],
                (unsigned char)info.xid.data[5],
                (unsigned char)info.xid.data[6],
                (unsigned char)info.xid.data[7]);

            printf("xid=[%s] \n", h_xid);

    } else {
        printf("Not in transaction \n");
        tpreturn(TPFAIL, -1, NULL, 0, 0);
    }


    EXEC SQL
    INSERT INTO tmax( empno, ename, job, hiredate,sal, xid)
    VALUES (:h_empno, :h_ename, :h_job, to_date(:h_date,'yymmdd'), :h_sal, :h_xid );

    if ( sqlca.sqlcode != SQLOK )
    {
        sprintf(tmp, "[%s] ORACLE tmax Insert Fail", msg->name);
        strcpy(rcvbuf, tmp);
        printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
        tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
    }

    sprintf(tmp, "[%s] ORACLE tmax Insert Success", msg->name);
    strcpy(rcvbuf, tmp);
    return 0;
}

SVC2301TX_2_oracle( TPSVCINFO *msg )
{
    str  sndbuf;
    char *rcvbuf;
    char tmp[1024];

    sndbuf = (str)msg->data;
    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
         printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
         tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    h_empno = h_sal = 0;

    memset( h_ename, 0x00, sizeof( h_ename ) );
    memset( h_job, 0x00, sizeof( h_job ) );
    memset( h_date, 0x00, sizeof( h_date ) );
    memset( tmp , 0x00, sizeof( tmp  ) );

    h_empno = sndbuf->empno;
    h_sal   = sndbuf->sal;
    strcpy( h_ename, sndbuf->ename );
    strcpy( h_job  , sndbuf->job   );
    strcpy( h_date , sndbuf->date  );


    EXEC SQL UPDATE tmax
        SET     empno = :h_empno+1,
                ename = :h_ename,
                job   = 'UPDATE'
        WHERE   empno = :h_empno;

    if ( sqlca.sqlcode != SQLOK )
    {
        sprintf(tmp, "[%s] ORACLE tmax Update Fail", msg->name);
        strcpy(rcvbuf, tmp);
        printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
        tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
    }

    sprintf(tmp, "[%s] ORACLE tmax Update Success", msg->name);
    strcpy(rcvbuf, tmp);
    return 0;
}

SVC2301TX_3_oracle( TPSVCINFO *msg )
{
    str  sndbuf;
    char *rcvbuf;
    char tmp[1024];

    sndbuf = (str)msg->data;
    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
        printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
        tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    h_empno = h_sal = 0;

    memset( h_ename, 0x00, sizeof( h_ename ) );
    memset( h_job, 0x00, sizeof( h_job ) );
    memset( h_date, 0x00, sizeof( h_date ) );
    memset( tmp , 0x00, sizeof( tmp  ) );

    h_empno = sndbuf->empno;
    h_sal   = sndbuf->sal;
    strcpy( h_ename, sndbuf->ename );
    strcpy( h_job  , sndbuf->job   );
    strcpy( h_date , sndbuf->date  );


    EXEC SQL DELETE tmax
        WHERE   empno = :h_empno;

    if ( sqlca.sqlcode != SQLOK )
    {
        sprintf(tmp, "[%s] ORACLE tmax Delete Fail", msg->name);
        strcpy(rcvbuf, tmp);
        printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
        tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
    }

    sprintf(tmp, "[%s] ORACLE tmax Delete Success", msg->name);
    strcpy(rcvbuf, tmp);
    return 0;
}

SVC2301TX_4_oracle( TPSVCINFO *msg )
{
    str  sndbuf;
    char *rcvbuf;
    char tmp[1024];

    sndbuf = (str)msg->data;
    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
        printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
        tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    h_empno = h_sal = h_count = 0;

    memset( h_ename, 0x00, sizeof( h_ename ) );
    memset( h_job, 0x00, sizeof( h_job ) );
    memset( h_date, 0x00, sizeof( h_date ) );
    memset( tmp , 0x00, sizeof( tmp  ) );

    h_empno = sndbuf->empno;
    h_sal   = sndbuf->sal;
    strcpy( h_ename, sndbuf->ename );
    strcpy( h_job  , sndbuf->job   );
    strcpy( h_date , sndbuf->date  );

    EXEC SQL SELECT COUNT(*)
        INTO    :h_count
        FROM    tmax
        WHERE   empno = :h_empno;

    if ( ( sqlca.sqlcode != SQLOK ) && ( sqlca.sqlcode != SQLNOTFOUND ) )
    {
        sprintf(tmp, "[%s] ORACLE tmax Select Fail", msg->name);
        strcpy(rcvbuf, tmp);
        printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
        tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
    }

    sprintf(tmp, "[%s] ORACLE tmax Select Success [%d]", msg->name, h_count);
    strcpy(rcvbuf, tmp);
    return 0;
}
DB2 호출 함수

다음은 DB2를 호출하는 함수를 사용한 프로그램의 예제이다.

#include <stdio.h>
#include <ctype.h>
#include <usrinc/atmi.h>
#include <usrinc/tx.h>
#include "svr1.h"

EXEC SQL INCLUDE SQLCA;

EXEC SQL BEGIN DECLARE SECTION;
    sqlint32  h_empno;
    char h_ename[50];
    char h_job[50];
    char h_date[50];
    float h_sal;
    sqlint32  h_count;
    char h_xid[100];
EXEC SQL END DECLARE SECTION;

SVC2301TX_1_db2( TPSVCINFO *msg )
{
    str  sndbuf;
    char *rcvbuf;
    char tmp[1024];
    TXINFO info;

    sndbuf = (str)msg->data;
    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
        printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
        tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    h_empno = h_sal = 0;

    memset( h_ename, 0x00, sizeof( h_ename ) );
    memset( h_job, 0x00, sizeof( h_job ) );
    memset( h_date, 0x00, sizeof( h_date ) );
    memset( tmp , 0x00, sizeof( tmp  ) );
    memset( h_xid , 0x00, sizeof( h_xid  ) );

    h_empno = sndbuf->empno;
    h_sal   = sndbuf->sal;
    strcpy( h_ename, sndbuf->ename );
    strcpy( h_job  , sndbuf->job   );
    strcpy( h_date , sndbuf->date  );

    if (tx_info(&info)==1) {
            sprintf(h_xid, "%x %x %x %x - %x %x %x %x",
                (unsigned char)info.xid.data[0],
                (unsigned char)info.xid.data[1],
                (unsigned char)info.xid.data[2],
                (unsigned char)info.xid.data[3],
                (unsigned char)info.xid.data[4],
                (unsigned char)info.xid.data[5],
                (unsigned char)info.xid.data[6],
                (unsigned char)info.xid.data[7]);

            printf("xid=[%s] \n", h_xid);

    } else {
        printf("Not in transaction \n");
        tpreturn(TPFAIL, -1, NULL, 0, 0);
    }


    EXEC SQL
    INSERT INTO tmax( empno, ename, job, hiredate,sal, xid)
    VALUES (:h_empno, :h_ename, :h_job, to_date(:h_date,'yymmdd'), :h_sal, :h_xid );

    if (sqlca.sqlcode != SQLOK)
    {
        sprintf(tmp, "[%s] DB2 tmax Insert Fail", msg->name);
        strcpy(rcvbuf, tmp);
        printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
        tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
    }

    sprintf(tmp, "[%s] DB2 tmax Insert Success", msg->name);
    strcpy(rcvbuf, tmp);
    return 0;
}

SVC2301TX_2_db2( TPSVCINFO *msg )
{
    str  sndbuf;
    char *rcvbuf;
    char tmp[1024];

    sndbuf = (str)msg->data;
    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
        printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
        tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    h_empno = h_sal = 0;

    memset( h_ename, 0x00, sizeof( h_ename ) );
    memset( h_job, 0x00, sizeof( h_job ) );
    memset( h_date, 0x00, sizeof( h_date ) );
    memset( tmp , 0x00, sizeof( tmp  ) );

    h_empno = sndbuf->empno;
    h_sal   = sndbuf->sal;
    strcpy( h_ename, sndbuf->ename );
    strcpy( h_job  , sndbuf->job   );
    strcpy( h_date , sndbuf->date  );


    EXEC SQL UPDATE tmax
        SET     empno = :h_empno+1,
                ename = :h_ename,
                job   = 'UPDATE'
        WHERE   empno = :h_empno;

    if (sqlca.sqlcode != SQLOK)
    {
        sprintf(tmp, "[%s] DB2 tmax Update Fail", msg->name);
        strcpy(rcvbuf, tmp);
        printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
        tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
    }

    sprintf(tmp, "[%s] DB2 tmax Update Success", msg->name);
    strcpy(rcvbuf, tmp);
    return 0;
}

SVC2301TX_3_db2( TPSVCINFO *msg )
{
    str  sndbuf;
    char *rcvbuf;
    char tmp[1024];

    sndbuf = (str)msg->data;
    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
        printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
        tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    h_empno = h_sal = 0;

    memset( h_ename, 0x00, sizeof( h_ename ) );
    memset( h_job, 0x00, sizeof( h_job ) );
    memset( h_date, 0x00, sizeof( h_date ) );
    memset( tmp , 0x00, sizeof( tmp  ) );

    h_empno = sndbuf->empno;
    h_sal   = sndbuf->sal;
    strcpy( h_ename, sndbuf->ename );
    strcpy( h_job  , sndbuf->job   );
    strcpy( h_date , sndbuf->date  );


    EXEC SQL DELETE tmax
        WHERE   empno = :h_empno;

    if (sqlca.sqlcode != SQLOK)
    {
        sprintf(tmp, "[%s] DB2 tmax Delete Fail", msg->name);
        strcpy(rcvbuf, tmp);
        printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
        tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
    }

    sprintf(tmp, "[%s] DB2 tmax Delete Success", msg->name);
    strcpy(rcvbuf, tmp);
    return 0;
}

SVC2301TX_4_db2( TPSVCINFO *msg )
{
    str  sndbuf;
    char *rcvbuf;
    char tmp[1024];

    sndbuf = (str)msg->data;
    rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
    if(rcvbuf==NULL) {
        printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
        tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    h_empno = h_sal = h_count = 0;

    memset( h_ename, 0x00, sizeof( h_ename ) );
    memset( h_job, 0x00, sizeof( h_job ) );
    memset( h_date, 0x00, sizeof( h_date ) );
    memset( tmp , 0x00, sizeof( tmp  ) );

    h_empno = sndbuf->empno;
    h_sal   = sndbuf->sal;
    strcpy( h_ename, sndbuf->ename );
    strcpy( h_job  , sndbuf->job   );
    strcpy( h_date , sndbuf->date  );


    EXEC SQL SELECT COUNT(*)
        INTO    :h_count
        FROM    tmax
        WHERE   empno = :h_empno;

    if ( ( sqlca.sqlcode != SQLOK ) && ( sqlca.sqlcode != SQLNOTFOUND ) )
    {
        sprintf(tmp, "[%s] DB2 tmax Select Fail", msg->name);
        strcpy(rcvbuf, tmp);
        printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
        tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
    }

    sprintf(tmp, "[%s] DB2 tmax Select Success [%d]", msg->name, h_count);
    strcpy(rcvbuf, tmp);
    return 0;
}

Multiple RM에서는 xa dynamic registration을 지원하지 않기 때문에 업무적으로 select 문을 호출하더라도 항상 tx_begin()을 호출해야 한다. 또는 select 문을 처리하는 서비스를 insert, update, delete를 수행하는 서비스를 가지지 않는 별도의 서버로 분리해서 처리해야 정상 동작한다.

3.2. 환경설정 및 컴파일

Oracle과 Tibero를 사용하는 MultipleRM의 환경설정 및 컴파일 예제를 설명한다.

환경설정

다음은 MultipleRM 서버의 환경설정 파일의 예제이다.

*DOMAIN

dom1        SHMKEY =@SHMEMKY@,    MINCLH=1, MAXCLH=1,
        TPORTNO=@TPORTNO@,    BLOCKTIME=60,
        RACPORT = @TRACPORT@,
        MAXTMS=50

*NODE
@HOSTNAME@    TMAXDIR="@TMAXDIR@",
        APPDIR="@TMAXDIR@/appbin",
        PATHDIR = "@TMAXDIR@/path",
        TLOGDIR = "@TMAXDIR@/log/tlog",
        ULOGDIR="@TMAXDIR@/log/ulog",
        SLOGDIR="@TMAXDIR@/log/slog",

*SVRGROUP
svg12301X    NODENAME = @HOSTNAME@,
        DBNAME = ORACLE,
        OPENINFO="Oracle_Xa+Acc=P/scott/tiger+SesTm=60+DbgFl=7+LogDir=@TMAXDIR@/log/xalog",
        TMSNAME = tms_ora,
        SVGTYPE = STMAX

svg12302X    NODENAME = @HOSTNAME@,
        DBNAME = DB2_STATIC,
        OPENINFO = "db=tptest,uid=tmaxha,pwd=ha0115",
        TMSNAME = tms_db2,
        SVGTYPE = STMAX


svgm1    NODENAME = @HOSTNAME@,
        SVGLIST = "svg12301X, svg12302X",
        SVGTYPE = MTMAX

*SERVER
# Common Insert/Update/Delete/Select
svr2301TX       SVGNAME = svgm1, MIN=5, MAX=5, MAXRSTART=-1
svr2301TX_UCS       SVGNAME = svgm1, MIN=5, MAX=5, MAXRSTART=-1, SVRTYPE=UCS

# Server Transaction
svr2311TX       SVGNAME = svg12301X

# tpforward
svr2321TX       SVGNAME = svg12301X
svr2322TX       SVGNAME = svg12301X

# AUTOTRAN
svr2331TX       SVGNAME = svg12301X

*SERVICE
# Common Insert/Update/Delete/Select
SVC2301TX_1     SVRNAME = svr2301TX, SVCTIME=5
SVC2301TX_2     SVRNAME = svr2301TX, SVCTIME=5
SVC2301TX_3     SVRNAME = svr2301TX, SVCTIME=5
SVC2301TX_4     SVRNAME = svr2301TX, SVCTIME=5
SVC2301TX_1_UCS     SVRNAME = svr2301TX_UCS, SVCTIME=5
SVC2301TX_2_UCS     SVRNAME = svr2301TX_UCS, SVCTIME=5
SVC2301TX_3_UCS     SVRNAME = svr2301TX_UCS, SVCTIME=5
SVC2301TX_4_UCS     SVRNAME = svr2301TX_UCS, SVCTIME=5
Makefile

Makefile 내의 LIBS(TMAX라이브러리)에 반드시 -lnodb를 포함해야 한다.

다음은 64bit Linux에서 MultipleRM 서버 프로그램을 컴파일하기 위한 Makefile의 예제이다.

# Oracle Env
include $(ORACLE_HOME)/precomp/lib/env_precomp.mk
ORALIBDIR = $(LIBHOME)
ORALIB = $(PROLDLIBS)

# DB2 Env
DB2LIBDIR = $(DB2_HOME)/lib
DB2LIBS   = -ldb2

DB      = TPTEST
DB2USER = tmaxha
DB2PASS = ha0115


# Server makefile

TARGET    = $(COMP_TARGET)
APOBJS    = $(TARGET).o
NSDLOBJ = $(TMAXDIR)/lib64/sdl.o
AP_ORA_OBJS = $(TARGET)_ora.o
AP_DB2_OBJS = $(TARGET)_db2.o

LIBS    = -lsvr  -lnsl -lnodb
OBJS    = $(AP_ORA_OBJS) $(AP_DB2_OBJS) $(APOBJS) $(SVCTOBJ)
SVCTOBJ = $(TARGET)_svctab.o

CFLAGS  =  -O -I$(TMAXDIR)

APPDIR  = $(TMAXDIR)/appbin
SVCTDIR = $(TMAXDIR)/svct
TMAXLIBDIR  = $(TMAXDIR)/lib64

#
.SUFFIXES : .c

.c.o:
    $(CC) $(CFLAGS) -c $<

#
# server compile
#
all: $(TARGET)

$(TARGET): $(OBJS)
    echo $(ORALIBDIR)
    $(CC) $(CFLAGS) -L$(TMAXLIBDIR) -o $(TARGET) -L$(DB2LIBDIR) -L$(ORALIBDIR) $(ORALIB) $(DB2LIBS) $(OBJS) $(LIBS) $(NSDLOBJ)
    mv $(TARGET) $(APPDIR)/.
    rm -f $(OBJS)

$(AP_ORA_OBJS):
    echo $(OBJS)
    proc iname=$(TARGET)_ora.pc include=$(TMAXDIR)
    $(CC) $(CFLAGS) -c $(TARGET)_ora.c

$(AP_DB2_OBJS):
    echo $(OBJS)
    db2 connect to $(DB) user $(DB2USER) using $(DB2PASS)
    db2 prep $(TARGET)_db2.sqc bindfile
    db2 bind $(TARGET)_db2.bnd
    db2 connect reset
    db2 terminate
    $(CC) $(CFLAGS) $(LDFLAGS)  -I$(DB2_HOME)/include -c $(TARGET)_db2.c

$(APOBJS): $(TARGET).c
    $(CC) $(CFLAGS) -c $(TARGET).c

$(SVCTOBJ):
    cp -f $(SVCTDIR)/$(TARGET)_svctab.c .
    touch ./$(TARGET)_svctab.c
    $(CC) $(CFLAGS) -c ./$(TARGET)_svctab.c

#
clean:
    -rm -f *.o core $(APPDIR)/$(TARGET)

운영체제에 따라 Makefile 내용은 다를 수 있다.

4. Oracle과 MQ 사용

서버 라이브러리를 이용하여 TCS에서 Oracle과 MQ를 사용할 수 있다. 본 절에서는 Oracle과 MQ를 사용하는 프로그램의 예제와 환경설정, 컴파일을 위한 Makefile의 예제를 설명한다.

4.1. 프로그램 예제

다음은 Oracle과 MQ를 함께 사용하는 TCS 예제 프로그램이다. Embedded SQL을 이용하여 서버를 작성하고 각 데이터베이스 벤더에서 제공하는 프리 컴파일러로 컴파일하여 구성할 수 있는 프로그램 예제이다.

MQ 호출 함수

다음은 MQ를 호출하는 함수를 사용한 프로그램의 예제이다.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmqc.h>
#include <getopt.h>
#include <usrinc/atmi.h>
#include <usrinc/tmaxapi.h>
#include <usrinc/fbuf.h>
#include <usrinc/tx.h>
#include "../sdl/demo_sdl.h"
#include "../fdl/demo_fdl.h"

#define MQ_CSP_PASSWORD_LENGTH         256

#define DATA_LEN 16


MQHCONN  Hcon;                   /* connection handle             */
MQHOBJ   Hobj;                   /* object handle                 */
char     QMName[50];             /* queue manager name            */
char     QName[50];              /* queue name                    */
MQOD     od = {MQOD_DEFAULT};    /* Object Descriptor             */
MQLONG   OpenCode = MQCC_FAILED; /* MQOPEN completion code        */
MQLONG   CReason;                /* reason code for MQCONNX       */

int      first;

int tpsvrinit(int argc, char **argv)
{
        /*   Declare MQI structures needed                                */
        MQCNO   cno = {MQCNO_DEFAULT};   /* connection options            */
        MQCSP   csp = {MQCSP_DEFAULT};   /* security parameters           */
        MQLONG   O_options;              /* MQOPEN options                */
        MQLONG   CompCode;               /* completion code               */
        MQLONG   Reason;                 /* reason code                   */
        char    *UserId = NULL;                 /* UserId for authentication     */
        char     Password[MQ_CSP_PASSWORD_LENGTH + 1] = {0}; /* For auth  */
        int                c;

        strcpy(QMName, "QM1");
        strcpy(QName, "Q1");

        opterr = 0;
        while ((c = getopt(argc, argv, "m:q:u:p:o:OE")) != EOF) {
                switch (c) {
                        case 'm':
                                strcpy(QMName, optarg);
                                break;
                        case 'q':
                                strcpy(QName, optarg);
                                break;
                        case 'u':
                                UserId = optarg;
                                break;
                        case 'p':
                                strcpy(Password, optarg);
                                break;
                        case 'O':
                                first = 0;
                                break;
                        case 'E':
                                first = 1;
                                break;
                        case '?':
                        default:
                                break;
                }
        }

        if (UserId != NULL) {
                cno.SecurityParmsPtr = &csp;
                cno.Version = MQCNO_VERSION_5;

                csp.AuthenticationType = MQCSP_AUTH_USER_ID_AND_PWD;
                csp.CSPUserIdPtr = UserId;
                csp.CSPUserIdLength = strlen(UserId);
                csp.CSPPasswordPtr = Password;
                csp.CSPPasswordLength = strlen(csp.CSPPasswordPtr);
        }

        MQCONNX(QMName,                 /* queue manager                  */
                &cno,                    /* connection options             */
                &Hcon,                   /* connection handle              */
                &CompCode,               /* completion code                */
                &CReason);               /* reason code                    */
        if (CompCode == MQCC_FAILED) {
                printf("MQCONNX ended with reason code %d\n", CReason);
                return -1;
        } else if (CompCode == MQCC_WARNING) {
                printf("MQCONNX generated a warning with reason code %d\n", CReason);
                printf("Continuing...\n");
        }

        strncpy(od.ObjectName, QName, (size_t)MQ_Q_NAME_LENGTH);
        printf("target queue is %s\n", od.ObjectName);

#if 0
        if (argc > 5)
        {
                strncpy(od.ObjectQMgrName, argv[5], (size_t) MQ_Q_MGR_NAME_LENGTH);
                printf("target queue manager is %s\n", od.ObjectQMgrName);
        }

        if (argc > 6)
        {
                strncpy(od.DynamicQName, argv[6], (size_t) MQ_Q_NAME_LENGTH);
                printf("dynamic queue name is %s\n", od.DynamicQName);
        }
#endif

        /*   Open the target message queue for output                 */
        O_options = MQOO_OUTPUT          /* open queue for output     */
                | MQOO_INPUT_AS_Q_DEF    /* open queue for input      */
                | MQOO_FAIL_IF_QUIESCING /* but not if MQM stopping   */
                ;                        /* = 0x2010 = 8208 decimal   */

        MQOPEN(Hcon,                      /* connection handle            */
                &od,                       /* object descriptor for queue  */
                O_options,                 /* open options                 */
                &Hobj,                     /* object handle                */
                &OpenCode,                 /* MQOPEN completion code       */
                &Reason);                  /* reason code                  */
        if (Reason != MQRC_NONE) {
                printf("MQOPEN ended with reason code %d\n", Reason);
        }
        if (OpenCode == MQCC_FAILED) {
                printf("unable to open queue for output\n");
        }
        return 0;
}

int tpsvrdone()
{
        MQLONG   C_options;              /* MQCLOSE options               */
        MQLONG   CompCode;               /* completion code               */
        MQLONG   Reason;                 /* reason code                   */

        /*   Close the target queue (if it was opened)                    */
        if (OpenCode != MQCC_FAILED) {
                C_options = MQCO_NONE;        /* no close options             */
                MQCLOSE(Hcon,                   /* connection handle            */
                        &Hobj,                  /* object handle                */
                        C_options,
                        &CompCode,              /* completion code              */
                        &Reason);               /* reason code                  */

                /* report reason, if any     */
                if (Reason != MQRC_NONE) {
                        printf("MQCLOSE ended with reason code %d\n", Reason);
                }
        }

        /*   Disconnect from MQM if not already connected                 */
        if (CReason != MQRC_ALREADY_CONNECTED) {
                MQDISC(&Hcon,                   /* connection handle            */
                        &CompCode,               /* completion code              */
                        &Reason);                /* reason code                  */

                /* report reason, if any     */
                if (Reason != MQRC_NONE) {
                        printf("MQDISC ended with reason code %d\n", Reason);
                }
        }
        return 0;
}

INSERT_MQ(TPSVCINFO *msg)
{
        MQMD     md = {MQMD_DEFAULT};    /* Message Descriptor            */
        MQPMO   pmo = {MQPMO_DEFAULT};   /* put message options           */
        MQLONG   CompCode;               /* completion code               */
        MQLONG   Reason;                 /* reason code                   */

        memcpy(md.Format,           /* character string format            */
                MQFMT_STRING, (size_t)MQ_FORMAT_LENGTH);

        pmo.Options = MQPMO_SYNCPOINT
                | MQPMO_FAIL_IF_QUIESCING;
        /* pmo.Options |= MQPMO_NEW_MSG_ID;                               */
        /* pmo.Options |= MQPMO_NEW_CORREL_ID;                            */

        memcpy(md.MsgId, MQMI_NONE, sizeof(md.MsgId) );

        MQPUT(Hcon,                /* connection handle               */
                Hobj,                /* object handle                   */
                &md,                 /* message descriptor              */
                &pmo,                /* default options (datagram)      */
                msg->len,             /* message length                  */
                msg->data,              /* message buffer                  */
                &CompCode,           /* completion code                 */
                &Reason);            /* reason code                     */
        if (Reason != MQRC_NONE) {
                printf("MQPUT ended with reason code %d\n", Reason);
        }
        if (CompCode != MQCC_FAILED)
                tpreturn(TPSUCCESS, 0, msg->data, msg->len, 0);
        else
                tpreturn(TPFAIL, 0, msg->data, msg->len, 0);
}

INSERT_DB(TPSVCINFO *msg)
{
        MQMD     md = {MQMD_DEFAULT};    /* Message Descriptor            */
        MQGMO   gmo = {MQGMO_DEFAULT};   /* get message options           */
        MQLONG   messlen;                /* message length                */
        MQLONG   CompCode;               /* completion code               */
        MQLONG   Reason;                 /* reason code                   */
        char *rcvbuf;
        long rcvlen;

        rcvbuf = tpalloc("STRING", NULL, 50000);
        if (rcvbuf == NULL)
                tpreturn(TPFAIL, 0, NULL, 0, 0);

        /*gmo.Version = MQGMO_VERSION_2;*/ /* Avoid need to reset Message */
        /*gmo.MatchOptions = MQMO_NONE; */ /* ID and Correlation ID after */
        /* every MQGET                 */
        gmo.Options = MQGMO_WAIT           /* wait for new messages       */
                | MQPMO_SYNCPOINT
                | MQGMO_CONVERT;       /* convert if necessary        */
        gmo.WaitInterval = 15000;          /* 15 second limit for waiting */

        rcvlen = 50000 -1; /* buffer size available for GET   */

        memcpy(md.MsgId, MQMI_NONE, sizeof(md.MsgId));
        memcpy(md.CorrelId, MQCI_NONE, sizeof(md.CorrelId));
        md.Encoding       = MQENC_NATIVE;
        md.CodedCharSetId = MQCCSI_Q_MGR;

        MQGET(Hcon,                /* connection handle                 */
                Hobj,                /* object handle                     */
                &md,                 /* message descriptor                */
                &gmo,                /* get message options               */
                rcvlen,              /* buffer length                     */
                rcvbuf,              /* message buffer                    */
                &messlen,            /* message length                    */
                &CompCode,           /* completion code                   */
                &Reason);            /* reason code                       */

        /* report reason, if any     */
        if (Reason != MQRC_NONE) {

                if (Reason == MQRC_NO_MSG_AVAILABLE) {  /* special report for normal end */
                        printf("no more messages\n");
                } else {                      /* general report for other reasons */
                        printf("MQGET ended with reason code %d\n", Reason);

                        /*   treat truncated message as a failure for this sample   */
                        if (Reason == MQRC_TRUNCATED_MSG_FAILED) {
                                CompCode = MQCC_FAILED;
                        }
                }
        }

        if (CompCode != MQCC_FAILED) {
                msg->data = rcvbuf;
                msg->len = rcvlen;
                INSERT_ESQL(msg);
                tpreturn( TPSUCCESS, 0, NULL, 0, 0 );
        } else {
                tpreturn(TPFAIL, 0, NULL, 0, 0);
        }
}
ORACLE 호출 함수

다음은 ORACLE를 호출하는 함수를 사용한 프로그램의 예제이다.

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <usrinc/atmi.h>

EXEC SQL include sqlca.h;

EXEC SQL begin declare section;
        int  h_empno;
        char h_ename[11];
        char h_job[10];
        int  h_mgr;
        char h_date[11];
        float h_sal;
        float h_comm;
        int  h_deptno;
EXEC SQL end declare section;

INSERT_ESQL( TPSVCINFO *msg )
{
        int i;

        h_empno = h_mgr = h_sal = h_comm = h_deptno = 0;

        memset( h_ename, 0x00, sizeof( h_ename ) );
        memset( h_job, 0x00, sizeof( h_job ) );
        memset( h_date, 0x00, sizeof( h_date ) );

        strncpy(h_job, msg->name, 9);
        strncpy(h_ename, msg->data, 10);
        for(i = 0; i < sizeof(h_ename); i++)
                h_ename[i] = toupper(h_ename[i]);
        strcpy(h_date, "160101");
        h_empno = time(NULL) % 10000;

        EXEC SQL DECLARE ESQL DATABASE;

        EXEC SQL AT ESQL INSERT
        INTO emp( empno, ename, job, hiredate)
        VALUES ( :h_empno, :h_ename, :h_job, to_date(:h_date,'yymmdd'));

        if ( sqlca.sqlcode != 0 ){
                printf( "insert failed sqlcode = %d\n",sqlca.sqlcode );
                tpreturn( TPFAIL, -1, NULL, 0, 0 );
        }

        printf("INSERT_ESQL insert success\n");
        if (!strcmp(msg->name, "INSERT_ESQL"))
                tpreturn( TPSUCCESS, 0, NULL, 0, 0 );
}

4.2. 환경설정 및 컴파일

Oracle과 MQ를 사용하는 MultipleRM의 환경설정 및 컴파일 예제를 설명한다.

환경설정

다음은 MultipleRM 서버의 환경설정 파일의 예제이다.

*DOMAIN
tmax1           SHMKEY=87789,
                MINCLH=1,
                MAXCLH=1,
                TPORTNO=7789,
                BLOCKTIME=5,
                TXTIME=10,
                RACPORT=3378,
                MAXCPC = 150
*NODE
qpsx2      TMAXDIR = "/home/tmax2/tmax",
                APPDIR  = "/home/tmax2/tmax/appbin",
                PATHDIR = "/home/tmax2/tmax/path",
                TLOGDIR = "/home/tmax2/tmax/log/tlog",
                ULOGDIR = "/home/tmax2/tmax/log/ulog",
                SLOGDIR = "/home/tmax2/tmax/log/slog"

*SVRGROUP
svg_mq           NODENAME = "qpsx2", DBNAME = MQ,
                 OPENINFO = "QMNAME=MQTEST",
                 TMSNAME = tms_mq, SVGTYPE = STMAX, RMID = 222,
                 MINTMS=1

svg_eora         NODENAME = "qpsx2", DBNAME = ORACLE,
                 OPENINFO = "Oracle_XA+Acc=P/scott/tiger+SesTm=60+LogDir=/home/tmax2/tmax/log/ulog+DbgFl=0x7+DB=ESQL",
                 TMSNAME = tms_ora, SVGTYPE = STMAX, RMID = 333,
                 MINTMS=1

svg_xa           NODENAME = "qpsx2", SVGTYPE = MTMAX,
                 SVGLIST = "svg_mq,svg_eora"

*SERVER
mrm_ora_mq       SVGNAME = svg_xa, CLOPT = "-- -m MQTEST -q ORANGE.LOCAL.QUEUE -E",
                 MAXRSTART = -1

*SERVICE
# Common PUT/GET
INSERT_DB        SVRNAME = mrm_ora_mq
INSERT_MQ        SVRNAME = mrm_ora_mq
Makefile

Makefile 내의 LIBS(TMAX라이브러리)에 반드시 -lnodb를 포함해야 한다.

다음은 32bit Linux에서 MultipleRM 서버 프로그램을 컴파일하기 위한 Makefile의 예제이다.

# Oracle Env
include $(ORACLE_HOME)/precomp/lib/env_precomp.mk
ORALIBDIR = $(LIBHOME)
ORALIB = $(PROLDLIBS)

LIBDIR = lib32

# Server makefile

TARGET        = $(COMP_TARGET)
TARGET2        = $(COMP_TARGET2)
APOBJS        = $(TARGET).o
APOBJS2        = $(TARGET2).o
NSDLOBJ = $(TMAXDIR)/lib/sdl.o

MQLIBD  = $(MQ_HOME)/lib
#MQLIB = -lmqm                # Server for C
#MQLIB = -lmqm_r        # Server for C for threaded
#MQLIB = -lmqmxa        # Server XA interface
#MQLIB = -lmqmxa_r        # Server XA interface for threaded
LIBS        = -lsvr  -lnsl -lnodb -lmqs -lmqmxa_r -lmqm_r -lm
OBJS    = $(APOBJS) $(APOBJS2) $(SVCTOBJ)
SVCTOBJ = $(TARGET)_svctab.o

CFLAGS  =  -v -m32 -O -I$(TMAXDIR) -I$(MQ_HOME)/inc

APPDIR  = $(TMAXDIR)/appbin
SVCTDIR = $(TMAXDIR)/svct
TMAXLIBDIR  = $(TMAXDIR)/lib

#
.SUFFIXES : .c

.c.o:
        $(CC) $(CFLAGS) -c $<

#
# server compile
#
all: $(TARGET)

$(TARGET): $(OBJS)
        $(CC) $(CFLAGS) -L$(TMAXLIBDIR) -o $(TARGET) -L$(MQLIBD) -L$(ORALIBDIR) $(ORALIB)  $(OBJS) $(LIBS) $(NSDLOBJ)
        mv $(TARGET) $(APPDIR)/.
        rm -f $(OBJS)

$(APOBJS2): $(TARGET2).pc
        proc iname=$(TARGET2) include=$(TMAXDIR)  define=__LINUX_ORACLE_PROC__
        $(CC) $(CFLAGS) -c $(TARGET2).c


$(SVCTOBJ):
        cp -f $(SVCTDIR)/$(TARGET)_svctab.c .
        touch ./$(TARGET)_svctab.c
        $(CC) $(CFLAGS) -c ./$(TARGET)_svctab.c

#
clean:
        -rm -f *.o core $(APPDIR)/$(TARGET)

운영체제에 따라 Makefile 내용은 다를 수 있다.

TMS Makefile

다음은 32bit Linux에서 ORACLE TMS을 컴파일하기 위한 Makefile의 예제이다.

# TMS Makefile for Oracle
# Linux

include $(ORACLE_HOME)/precomp/lib/env_precomp.mk
ORALIBDIR = $(LIBHOME)
ORALIB = $(PROLDLIBS) $(LIBCLNTSH)
LIBDIR = lib32

TARGET  = tms_ora
APOBJ   = dumy.o

APPDIR        = $(TMAXDIR)/appbin
TMAXLIBD= $(TMAXDIR)/lib
TMAXLIBS= -ltms -loras
#TMAXLIBS= -ltmsd -lorasd

CFLAGS  = -g -v -m32
CFLAGS  =  -m32
LDFLAGS =
SYSLIBS =

all : $(TARGET)

$(TARGET): $(APOBJ)
        $(CC) $(CFLAGS) $(LDFLAGS) -o $(TARGET) -L$(TMAXLIBD) $(TMAXLIBS) $(APOBJ) -L$(ORALIBDIR) $(ORALIB) $(SYSLIBS)
        mv $(TARGET) $(APPDIR)/.

$(APOBJ):
        $(CC) $(CFLAGS) -c dumy.c
#
clean:
        -rm -f *.o core $(APPDIR)/$(TARGET)

다음은 32bit Linux에서 MQ TMS을 컴파일하기 위한 Makefile의 예제이다.

# TMS Makefile for MQ
# Linux

MQLIBDIR = $(MQ_HOME)/lib
#MQLIB = -lmqmxa        # Server XA interface
MQLIB = -lmqmxa_r        # Server XA interface for threaded

TARGET  = tms_mq
APOBJ   = dumy.o

APPDIR  = $(TMAXDIR)/appbin
TMAXLIBD= $(TMAXDIR)/lib
TMAXLIBS= -ltms -lmqs

CFLAGS  = -m32
LDFLAGS =
SYSLIBS =

all : $(TARGET)

$(TARGET): $(APOBJ)
        $(CC) $(CFLAGS) $(LDFLAGS) -o $(TARGET) -L$(TMAXLIBD) $(TMAXLIBS) $(APOBJ) -L$(MQLIBDIR) $(MQLIB)
        mv $(TARGET) $(APPDIR)/.

$(APOBJ):
        $(CC) $(CFLAGS) -c dumy.c
#
clean:
        -rm -f *.o core $(APPDIR)/$(TARGET)

5. Tibero와 Tibero 사용

서버 라이브러리를 이용하여 TCS에서 Tibero와 Tibero을 사용할 수 있다. 본 절에서는 Tibero와 Tibero을 사용하는 프로그램의 예제와 환경설정, 컴파일을 위한 Makefile의 예제를 설명한다.

5.1. 프로그램 예제

다음은 2개의 RM이 모두 Tibero이지만 인스턴스가 다른 경우에 한 서버에서 처리하는 예제이다.

같은 Tibero 클라이언트 라이브러리를 사용하기 때문에 사용자 코드에서 쿼리를 수행할 경우 어떤 인스턴트를 수행할지는 사용자가 지정해야 한다. 이를 위해서 Tibero에서는 ESQL문(EXEC SQL XA SET CONNECTION AT :conn_id)을 이용하여 해당하는 인스턴트 커넥션을 얻어와서 지정할 수 있다.

서비스 수행

다음은 서비스를 수행하는 프로그램의 소스이다.

#include <stdio.h>
#include <ctype.h>
#include <usrinc/atmi.h>
#include <usrinc/tx.h>

extern int SVC2301TX_1_tibero(TPSVCINFO *msg);

SVC2301TX_1( TPSVCINFO *msg )
{
     int ret;
     int fail = 0;
     char *rcvbuf;

     rcvbuf=(char *)tpalloc("STRING", NULL, 4096);

     if(rcvbuf==NULL) {
          printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
          tpreturn(TPFAIL, -1, NULL, 0, 0);
     }

     ret = SVC2301TX_1_tibero(msg);
     if (ret < 0) {
          fail = 1;
     }

     if ( fail ) {
          printf("SVC2301TX_1  FAIL\n");
          tpreturn(TPFAIL,-1,NULL, 0,0);
     }

     strcpy(rcvbuf, "Multiple RM Insert Success");
     tpreturn( TPSUCCESS, 0, rcvbuf, strlen(rcvbuf), 0 );
}
Tibero 호출 함수

다음은 Tibero을 호출하는 함수를 사용하는 프로그램의 예제이다.

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <usrinc/atmi.h>
#include <usrinc/tmaxapi.h>
#include <usrinc/fbuf.h>
#include <usrinc/tx.h>
#include "../sdl/demo_sdl.h"
#include "../fdl/demo_fdl.h"

#define SQLOK       0
#define SQLDUP      1
#define SQLNOTFOUND 1403

EXEC SQL INCLUDE SQLCA.H;
EXEC SQL BEGIN DECLARE SECTION;
     int  h_empno;
     char h_ename[MAXLEN];
     char h_job[MAXLEN];
     char h_date[MAXLEN];
     float h_sal;
     int  h_count;
     char h_xid[100];
     char h_conn_id[MAXLEN];
EXEC SQL END DECLARE SECTION;

SVC2301TX_1_tibero( TPSVCINFO *msg )
{
     str  sndbuf;
     char *rcvbuf;
     char tmp[1024];
     TXINFO info;

     sndbuf = (str)msg->data;
     rcvbuf=(char *)tpalloc("STRING", NULL, 4096);
     if(rcvbuf==NULL) {
          printf("tpalloc failed (rcvbuf) : %s\n", tpstrerror(tperrno));
          tpreturn(TPFAIL, -1, NULL, 0, 0);
     }

     h_empno = h_sal = 0;
     memset( h_ename, 0x00, sizeof( h_ename ) );
     memset( h_job, 0x00, sizeof( h_job ) );
     memset( h_date, 0x00, sizeof( h_date ) );
     memset( tmp , 0x00, sizeof( tmp  ) );
     memset( h_xid , 0x00, sizeof( h_xid  ) );
     memset( h_conn_id, 0x00, sizeof( h_conn_id ) );

     h_empno = sndbuf->empno;
     h_sal   = sndbuf->sal;
     strcpy( h_ename, sndbuf->ename );
     strcpy( h_job  , sndbuf->job   );
     strcpy( h_date , sndbuf->date  );

     if (tx_info(&info)==1) {
          sprintf(h_xid, "%x %x %x %x - %x %x %x %x",
          (unsigned char)info.xid.data[0],
          (unsigned char)info.xid.data[1],
          (unsigned char)info.xid.data[2],
          (unsigned char)info.xid.data[3],
          (unsigned char)info.xid.data[4],
          (unsigned char)info.xid.data[5],
          (unsigned char)info.xid.data[6],
          (unsigned char)info.xid.data[7]);
          printf("xid=[%s] \n", h_xid);
     } else {
          printf("Not in transaction \n");
          tpreturn(TPFAIL, -1, NULL, 0, 0);
     }

     /* Set Default SQL CTX with conn_id */
     strcpy(h_conn_id, "DB1");

     EXEC SQL XA SET CONNECTION AT :h_conn_id;
     if ( sqlca.sqlcode != SQLOK )
     {
          sprintf(tmp, "[%s] emp Insert Fail", msg->name);
          strcpy(rcvbuf, tmp);
          printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
          tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
     }

     EXEC SQL INSERT INTO tmax( empno, ename, job, hiredate,sal, xid)
     VALUES (:h_empno, :h_ename, :h_job, to_date(:h_date,'yymmdd'), :h_sal, :h_xid );
     if ( sqlca.sqlcode != SQLOK )
     {
         sprintf(tmp, "[%s] TIBERO tmax Insert Fail", msg->name);
         strcpy(rcvbuf, tmp);
         printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
         tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
     }
     sleep(1);

     /* Set Default SQL CTX with conn_id */
     strcpy(h_conn_id, "DB2");

     EXEC SQL XA SET CONNECTION AT :h_conn_id;
     if ( sqlca.sqlcode != SQLOK )
     {
          sprintf(tmp, "[%s] emp Insert Fail", msg->name);
          strcpy(rcvbuf, tmp);
          printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
          tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
     }

     EXEC SQL
     INSERT INTO tmax( empno, ename, job, hiredate,sal, xid)
     VALUES (:h_empno, :h_ename, :h_job, to_date(:h_date,'yymmdd'), :h_sal, :h_xid );
     if ( sqlca.sqlcode != SQLOK )
     {
          sprintf(tmp, "[%s] TIBERO tmax Insert Fail", msg->name);
          strcpy(rcvbuf, tmp);
          printf("[%s] %d \n", rcvbuf, sqlca.sqlcode);
          tpreturn( TPFAIL, sqlca.sqlcode, rcvbuf, strlen(rcvbuf), 0 );
     }

     sprintf(tmp, "[%s] TIBERO tmax Insert Success", msg->name);
     strcpy(rcvbuf, tmp);
     return 0;
}

5.2. 환경설정 및 컴파일

Tibero와 Tibero을 사용하는 MultipleRM의 환경설정 및 컴파일 예제를 설명한다.

환경설정

다음은 MultipleRM 서버의 환경설정 파일의 예제이다.

*DOMAIN
domain       SHMKEY   = 81522, MAXUSER = 10000,
             MINCLH   = 1, MAXCLH = 1,
             TPORTNO  = 11500,  BLOCKTIME = 30,
             MAXSACALL = 1024, MAXCACALL = 1024,
             MAXMTMAX = 3, MAXSTMAX = 10

*NODE
node1        TMAXDIR = "/home/tmax1/tmax",
             APPDIR  = "/home/tmax1/tmax/appbin",
             PATHDIR = "/home/tmax1/tmax/path",
             TLOGDIR = "/home/tmax1/tmax/log/tlog",
             ULOGDIR = "/home/tmax1/tmax/log/ulog",
             SLOGDIR = "/home/tmax1/tmax/log/slog"

node2        TMAXDIR = "/home/tmax2/tmax",
             APPDIR  = "/home/tmax2/tmax/appbin",
             PATHDIR = "/home/tmax2/tmax/path",
             TLOGDIR = "/home/tmax2/tmax/log/tlog",
             ULOGDIR = "/home/tmax2/tmax/log/ulog",
             SLOGDIR = "/home/tmax2/tmax/log/slog"

*SVRGROUP
svg_s1       NODENAME = node1,
             DBNAME = TIBERO,
             OPENINFO="TIBERO_XA:user=tibero,pwd=tmax,db=tibero1,Loose_Coupling=false,sestm=60,conn_id=DB1",
             TMSNAME = tms_tbr,
             SVGTYPE = STMAX,
             RMID = 1
svg_s2       NODENAME = node2,
             DBNAME = TIBERO,
             OPENINFO="TIBERO_XA:user=tibero,pwd=tmax,db=tibero2,Loose_Coupling=false,sestm=60,conn_id=DB2",
             TMSNAME = tms_tbr,
             SVGTYPE = STMAX,
             RMID = 2

svgm1        NODENAME = node1,
             SVGLIST = "svg_s1, svg_s2",
             SVGTYPE = MTMAX

*SERVER
svr2301TX    SVGNAME = svgm1, MIN=5, MAX=5, MAXRSTART=-1

*SERVICE
SVC2301TX_1  SVRNAME = svr2301TX, SVCTIME=5
Makefile

Makefile의 TMAXLIBS에 반드시 -lnodb를 포함해야 한다. 다음은 64bit Linux에서 MultipleRM 서버 프로그램을 컴파일하기 위한 Makefile의 예이다.

# Tibero Env
TBINC = $(TB_HOME)/client/include
TBLIBDIR = $(TB_HOME)/client/lib
TBLIB = -ltbxa -ltbertl -ltbcli -lm -lpthread

# Server makefile
TARGET = $(COMP_TARGET)
APOBJS = $(TARGET).o
NSDLOBJ = $(TMAXDIR)/lib64/sdl.o
AP_TBR_OBJS = $(TARGET)_tbr.o

LIBS = -lsvr  -lnsl -lnodb
OBJS    = $(AP_TBR_OBJS) $(APOBJS) $(SVCTOBJ)
SVCTOBJ = $(TARGET)_svctab.o
CFLAGS  =  -O -I$(TMAXDIR)

APPDIR  = $(TMAXDIR)/appbin
SVCTDIR = $(TMAXDIR)/svct
TMAXLIBDIR  = $(TMAXDIR)/lib64

#
.SUFFIXES : .c

.c.o:
    $(CC) $(CFLAGS) -c $<

#
# server compile
#
all: $(TARGET)

$(TARGET): $(OBJS)
    echo $(ORALIBDIR)
    $(CC) $(CFLAGS) -L$(TMAXLIBDIR) -o $(TARGET) -L$(TBLIBDIR) $(TBLIB) $(OBJS) $(LIBS) $(NSDLOBJ)
    mv $(TARGET) $(APPDIR)/.
    rm -f $(OBJS)

$(AP_TBR_OBJS):
    echo $(OBJS)
    tbpc iname=$(TARGET)_tbr.tbc include=$(TMAXDIR)
    $(CC) $(CFLAGS) -I$(TBINC) -c $(TARGET)_tbr.c

$(APOBJS): $(TARGET).c
    $(CC) $(CFLAGS) -c $(TARGET).c

$(SVCTOBJ):
    cp -f $(SVCTDIR)/$(TARGET)_svctab.c .
    touch ./$(TARGET)_svctab.c
    $(CC) $(CFLAGS) -c ./$(TARGET)_svctab.c

#
clean:
    -rm -f *.o core $(APPDIR)/$(TARGET)

운영체제에 따라 Makefile 내용은 다를 수 있다.