Examples

This chapter describes examples of X25GW service types.

1. OUTBOUND X25GW

In this example, the remote node sends a request and X25GW calls the user-specified service. When the service completes processing the request and sends the result to X25GW, X25GW sends the result back to the remote node. Configure X25GW by modifying custom.c according to the remote node environment.

The following diagram shows how X25GW operates in OUTBOUND mode.

figure 3 1
X25GW Operation in OUTBOUND X25 Mode

The program is composed of the following files.

Type File Name

Configuration File

x25gw.m, x25gw.cfg

X25GW

custom.c, custom.h

Server

svr.c

1.1. Environment File

<x25gw.m>
*DOMAIN
res            SHMKEY=88000,
               MINCLH=1,
               MAXCLH=1,
               TPORTNO=8888

*NODE
node1          TMAXDIR=”/home/tmax”,
               APPDIR=”/home/tmax/appbin”

*SVRGROUP
svg1           NODENAME=node1

*SERVER
x25gw          SVGNAME=svg1,
               MIN=1,
               MAX=1,
               CPC=5,
               SVRTYPE=CUSTOM_GATEWAY,
               CLOPT=”-- -F /home/tmax/config/x25gw.cfg”
svr            SVGNAME=svg1,
               MIN=1, MAX=1

*SERVICE
TOUPPER        SVRNAME=svr
<x25gw.cfg>
# gwno | link_no | start_LCN_no | num_LCN | dir | reply_dedicated| dev_path
#
0 1 1 4 any no /dev/x25pkt

1.2. X25GW

<custom.h>
#ifndef _CUSTOM_H_
#define _CUSTOM_H_

/* ---------------------------------------------------------- */
/*              Fixed structures and macros       */

#define MSG_MAGIC               “Tmax”
#define REMOTE_REQUEST           0
#define REMOTE_REPLY             1
#define SVC_NAME_LENGTH          20
#define SYSID_LENGTH             20

/* msg_info_t struct must not be not redefined. */
typedef struct msg_info {
    char  svc[SVC_NAME_LENGTH];
    int   err;
    int   len;
    int   uid;
    int   flags;
    int   msgtype;
    int   channel_id;
    char  sys_id[SYSID_LENGTH];
} msg_info_t;

typedef struct msg_body {
    char        name[16];
    char        data[100];
} msg_body_t;

#endif
<custom.c>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include “custom.h”

/* If the following functions are not required, the internal logic does not need to be implemented. */
/* However, they must be defined since they are used in the X25GW library. */
int init_remote_info(char *myname, int mynumber, int num_channel, int key)
{
    return 1;
}

int remote_connected(int index, int linkno, int lcnno, int type)
{
    return 1;
}

int get_msg_info(char *data, msg_info_t *info)
{
    msg_body_t *body;

    if ((info == NULL) || (data == NULL))
        return -1;

    body = (msg_body_t *)data;

    info->err   = 0;
    info->flags = 0;

    memset(info->svc, 0x00, SVC_NAME_LENGTH);
    strncpy(info->svc, body->name, 8);

    /* Return REMOTE_REQUEST to indicate that the request is from the remote node. */
    return REMOTE_REQUEST;
}

int get_service_name(char *header, int err, char *svc)
{
    return -1;
}

int put_msg_info(char *data, msg_info_t *info)
{
    msg_body_t *body;

    if ((info == NULL) || (data == NULL))
        return -1;

    body = (msg_body_t *)data;

    /* Send the error status in body->name */
    if (info->err)   /* error */
        strcpy(body->name, “Fail”);
    else
        strcpy(body->name, “Success”);

    /* Returns the data length to send the results to the remote node. */
    return info->len;
}

int get_channel_num(char *data)
{
    return -1;
}

int remote_closed(int index, int type)
{
    return 1;
}

int prepare_shutdown(int code)
{
    return 1;
}

int outmsg_recovery(char *data, msg_info_t *info)
{
    return -1;
}

int inmsg_recovery(char *data, msg_info_t *info)
{
    return -1;
}
<Makefile>
# This Makefile is for ibm 32bit.
#TARGET must be the same as the SERVER name defined in the Tmax environment file.
TARGET = x25gw
APOBJS = $(TARGET).o

#To create X25GW,libX25GW.a or libX25GW.so must be linked.
LIBS = -lx25gw -ltmaxgw
OBJS = custom.o register.o

CFLAGS = -q32 -O -I$(TMAXDIR) -D_DBG
LDFLAGS = -brtl

APPDIR = $(TMAXDIR)/appbin
LIBDIR = $(TMAXDIR)/lib

.SUFFIXES : .c

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

$(TARGET): $(OBJS)
    $(CC) $(CFLAGS) $(LDFLAGS) -L$(LIBDIR) -o $(TARGET) $(OBJS) $(LIBS)
    mv $(TARGET) $(APPDIR)/.
    rm -f $(OBJS)

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

1.3. Server

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


TOUPPER(TPSVCINFO *msg)
{
    int i;

    printf("TOUPPER service is started!\n");
    printf("INPUT : len=%d, data='%s'\n", msg->len, msg->data);

    for (i = 0; i < msg->len; i++)
        msg->data[i] = toupper(msg->data[i]);

    printf("OUTPUT: len=%d, data='%s'\n",
        strlen(msg->data), msg->data);

    tpreturn(TPSUCCESS,0,(char *)msg->data, msg->len, 0);
}

2. Synchronous INBOUND X25GW

When a Tmax client (or a Tmax service) initiates a tpcall to X25GW, X25GW sends data to the remote node, and returns the result to the client/service when the remote node sends a reply.

It is important to specify the UID when a service is requested from the Tmax client to the remote node. Save the UID value (info→uid) specified in the X25GW library in the message using the put_msg_info function, or save the user generated UID in the message and then set the UID value to the uid attribute. The message containing the UID is sent to the remote node, and the remote node must return the UID without modifying it. When X25GW receives the reply, it calls the get_msg_info function to get the UID value and returns the reply back the client/service that requested the service.

The following is how X25GW operates in synchronous inbound mode.

figure 3 2
X25GW Operation in Synchronous INBOUND Mode

The program is composed of the following files.

Category File Name

Configuration File

x25gw.m, x25gw.cfg

X25GW

custom.c, custom.h

Client

cli_x25gw.c

2.1. Environment File

<x25gw.m>
*DOMAIN
res        SHMKEY=88000,
           MINCLH=1,
           MAXCLH=1,
           TPORTNO=8888

*NODE
node1      TMAXDIR=”/home/tmax”,
           APPDIR=”/home/tmax/appbin”

*SVRGROUP
svg1       NODENAME=node1

*SERVER
x25gw      SVGNAME=svg1,
           MIN=1,
           MAX=1,
           CPC=5,
           SVRTYPE=CUSTOM_GATEWAY,
           CLOPT=”-- -F /home/tmax/config/x25gw.cfg”

*SERVICE
X25GW      SVRNAME=x25gw
<x25gw.cfg>
# gwno | link_no | start_LCN_no | num_LCN | dir | reply_dedicated| dev_path
#
0 1 1 4 any no /dev/x25pkt

2.2. X25GW

<custom.h>
#ifndef _CUSTOM_H_
#define _CUSTOM_H_

/* ---------------------------------------------------------- */
/*              Fixed structures and macros       */

#define MSG_MAGIC               “Tmax”
#define REMOTE_REQUEST          0
#define REMOTE_REPLY             1
#define SVC_NAME_LENGTH         20
#define SYSID_LENGTH              20

/* msg_info_t struct cannot be redefined. */
typedef struct msg_info {
    char  svc[SVC_NAME_LENGTH];
    int   err;
    int   len;
    int   uid;
    int   flags;
    int   msgtype;
    int   channel_id;
    char  sys_id[SYSID_LENGTH];
} msg_info_t;

/* ---------------------------------------------------------- */
/*        Modifiable structures and macros         */
/* msg_header_t and msg_body_t structs can be redefined. */
#define UID_FIELD 98
#define SVC_NAME_FIELD 92
#define UID_LENGTH 4
#define SVC_LENGTH 6

typedef struct msg_body {
    char        data[92];
    char        name[6];
    char        uid[4];
} msg_body_t;

#endif  /* _CUSTOM_H_ */
<custom.c>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include “custom.h”

/* If the following functions are not required, the internal logic does not need to be implemented. */
/* However, they must be defined since they are used in the X25GW library. */
int get_channel_num(char *data)
{
    return -1;
}

int put_msg_complete(char *hp, char *data, msg_info_t *info)
{
    return 1;
}

int get_service_name(char *header, int err, char *svc)
{
    return -1;
}

int init_remote_info(char *myname, int mynumber, int num_channel, int key)
{
    return 1;
}

int remote_connected(int index, int addr, int type, int fd)
{
    return 1;
}

int get_msg_info(char *data, msg_info_t *info)
{
        info->flags = 0;

        if ((info == NULL) || (data == NULL))
            return -1;

        info->err = 0;
        info->flags = 0;
        /* Set info->uid. */
        memcpy((char *)&(info->uid), (char *)&data[UID_FIELD], UID_LENGTH);

        strncpy(info->svc, (char *)&data[SVC_NAME_FIELD], SVC_LENGTH);
        info->svc[SVC_LENGTH] = 0;

        if (info->uid == 0) {
            return REMOTE_REQUEST;
        }
        else {
            return REMOTE_REPLY;
        }
}

int put_msg_info(char *data, msg_info_t *info)
{
        int  nSize = 0;

        if ((info == NULL) || (data == NULL))
            return -1;

        if (info->err) {
            printf(“info->err = %d\n”, info->err);
            return -1;
        }
        else
            /* When making a request to the remote node, this uid  */
            /* must be set to the value configured in the library.*/
            memcpy((char *)&data[UID_FIELD], (char *)&(info->uid),
                    UID_LENGTH);

        memcpy((char *)&data[SVC_NAME_FIELD], info->svc, SVC_LENGTH);
        return info->len;
}

int remote_closed(int index, int type)
{
    return 1;
}

int prepare_shutdown(int code)
{
    return 1;
}

int outmsg_recovery(char *data, msg_info_t *info)
{
    return -1;
}

int inmsg_recovery(char *data, msg_info_t *info)
{
    return -1;
}
<Makefile>
# This Makefile is for ibm 32bit.
#TARGET must be the same as the SERVER name defined in the Tmax environment file.
TARGET = x25gw
APOBJS = $(TARGET).o

#To create X25GW, #libX25GW.a or libX25GW.so must be linked.
LIBS = -lx25gw -ltmaxgw
OBJS = custom.o register.o

CFLAGS = -q32 -O -I$(TMAXDIR) -D_DBG
LDFLAGS = -brtl

APPDIR = $(TMAXDIR)/appbin
LIBDIR = $(TMAXDIR)/lib

.SUFFIXES : .c

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

$(TARGET): $(OBJS)
    $(CC) $(CFLAGS) $(LDFLAGS) -L$(LIBDIR) -o $(TARGET) $(OBJS) $(LIBS)
    mv $(TARGET) $(APPDIR)/.
    rm -f $(OBJS)

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

2.3. Client

<cli_x25gw.c>
/* This program makes service request to X25GW.
   This program is implemented after a connection is established between
   X25GW and the remote node. */
#include <stdio.h>
#include <usrinc/atmi.h>
#include "custom.h"

int main(int argc, char **argv)
{
    int ret;
    msg_body_t *body;
    char *buf;
    long rlen;

    ret = tmaxreadenv("tmax.env", "TMAX");
    if (ret < 0) {
        printf("tmaxreadenv fail...[%s]\n", tpstrerror(tperrno));
    }

    ret = tpstart((TPSTART_T *)NULL);
    if (ret < 0) {
        printf("tpstart fail...[%s]\n", tpstrerror(tperrno));
        return -1;
    }
    buf = tpalloc("STRING", 0, 0);
    if (buf == NULL){
        printf("buf tpalloc fail..[%s]\n", tpstrerror(tperrno));
        tpend();
        return -1;
    }
    body = (msg_body_t *)buf;
    memcpy(body->data, argv[1], strlen(argv[1]));
    body->data[51] = 0;

    /* Call X25GW service. */
    ret = tpcall("X25GW", buf, sizeof(msg_body_t), &buf, &rlen, 0);
    if (ret < 0){
        printf("tpcall fail...[%s]\n", tpstrerror(tperrno));
        tpfree((char *)buf);
        tpend();
        return -1;
    }
    body = (msg_body_t *)buf;
    printf("return value = %s\n", body->data);
    tpfree((char *)buf);
    tpend();
}

3. Non-blocking X25GW

The non-blocking X25GW method is often used when communicating with external institutions. This is because even with a small number of processes, it incurs less load on the system and can process more tasks. A blocking method calls X25GW directly from a Tmax client or service and receives a reply, but a non-blocking method uses separate sending and receiving processes to send a request to and process the reply from X25GW.

All processes that want to call X25GW must first call the sending process, and then the sending process pre-processes the request before calling X25GW via tpforward. Before processing the request, X25GW unblocks the channel connected to the Tmax engine and passes the request to the remote node. When X25GW receives a reply from the remote node, it finds and forwards the reply to the receiving service to process the reply in order. After the receiving process processes the reply, it sends the result to the initiating service requester.

Since the service that directly calls X25GW or the service that receives tprelay runs asynchronously, it incurs almost no overhead. However, the process that initially calls the sending service is blocked until it receives a reply.

The following diagram shows how a connection made between the remote node (server) and X25GW (client).

figure 3 3
X25GW Operation in Non-blocking Mode

The program is composed of the following files.

Type File Name

Configuration File

x25gw.m, x25gw.cfg

X25GW

custom.c, custom.h

Server

sndsvr.c, rcvsvr.c

Client

cli_x25gw.c, custom.h

3.1. Environment File

<x25gw.m >
*DOMAIN
res    SHMKEY=88000,
       MINCLH=1,
       MAXCLH=1,
       TPORTNO=8888

*NODE
node1  TMAXDIR=”/home/tmax”,
       APPDIR=”/home/tmax/appbin”

*SVRGROUP
svg1   NODENAME=node1

*SERVER
x25gw   SVGNAME=svg1,
        MIN=1,
        MAX=1,
        CPC=5,
        SVRTYPE=CUSTOM_GATEWAY,
        CLOPT=”-- -F /home/tmax/config/x25gw.cfg –S RECVSVC –H 9”
sndsvr  SVGNAME=svg1,
        MIN=1,
        MAX=1

rcvsvr  SVGNAME=svg1,
        MIN=1,
        MAX=1

*SERVICE
X25GW         SVRNAME=x25gw
SENDSVC        SVRNAME=sndsvr
RECVSVC        SVRNAME=rcvsvr
<x25gw.cfg>
# gwno | link_no | start_LCN_no | num_LCN | dir | reply_dedicated| dev_path
#
0    1    1    4    any    no    /dev/x25pkt

3.2. X25GW

<custom.h>
/* ---------------------- custom.h -------------------------- */

#ifndef _CUSTOM_H_
#define _CUSTOM_H_

/* ---------------------------------------------------------- */
/*              Fixed structures and macros                         */
/* Common of Agent Define */
#define MSG_MAGIC               “Tmax”
#define REMOTE_REQUEST          0
#define REMOTE_REPLY            1
#define SVC_NAME_LENGTH         20
#define SYSID_LENGTH             20

/* msg_info_t struct cannot be redefined. */
typedef struct msg_info {
    char  svc[SVC_NAME_LENGTH];
    int   err;
    int   len;
    int   uid;
    int   flags;
    int   msgtype;
    int   channel_id;
    char  sys_id[SYSID_LENGTH];
} msg_info_t;

/* ---------------------------------------------------------- */
/*              Modifiable structures and macros                   */

#define UID_FIELD               58
#define SVC_NAME_FIELD          52
#define UID_LENGTH 4
#define SVC_LENGTH 6
#define MSG_KEEP_SVC_SIZE       9

typedef struct msg_body {
    char        retsvcname[9]; /* The field that stores the data whose size is specified with the -H option */
    char        data[52];
    char        name[6];
    char        uid[4];
} msg_body_t;

/* The struct that excludes retsvcname, the internal header of the body,
   when communicating with the remote node. */
typedef struct remote_body{
    char        data[52];
    char        name[6];
    char        uid[4];
}remote_body_t;

#endif
<custom.c>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include “custom.h”

/* If the following functions are not required, the internal logic does not need to be implemented. */
/* However, they must be defined since they are used in the X25GW library. */
int get_channel_num(char *data)
{
    return -1;
}

/* Set the service name to tprelay to. */
int get_service_name(char *header, int err, char *svc)
{
    /*strcpy the internal user header (retsvcname).*/
    strcpy(svc, header);
    svc[8] = 0;
    return 1;
}

int init_remote_info(char *myname, int mynumber, int num_channel, int key)
{
    return 1;
}

int remote_connected(int index, int linkno, int lcnno, int type)
{
    return 1;
}

/* get_msg_info and put_msg_info must be implemented same as the
   synchronous client X25GW. */
int get_msg_info(char *data, msg_info_t *info)
{
        info->flags = 0;

        if ((info == NULL) || (data == NULL))
            return -1;

        info->err = 0;
        info->flags = 0;
        /* Set info->uid. */
        memcpy((char *)&(info->uid), (char *)&data[UID_FIELD], UID_LENGTH);

        strncpy(info->svc, (char *)&data[SVC_NAME_FIELD], SVC_LENGTH);
        info->svc[8] = 0;

        if (info->uid == 0) {
            return REMOTE_REQUEST;
        }
        else {
            return REMOTE_REPLY;
        }
}

int put_msg_info(char *data, msg_info_t *info)
{
        int  nSize = 0;

        if ((info == NULL) || (data == NULL))
            return -1;

        if (info->err) {
            printf(“info->err = %d\n”, info->err);
            return -1;
        }
        else
           /* When a request is made to the remote node, this uid must be configured */
            /* set to the value configured in the library.*/
            memcpy((char *)&data[UID_FIELD], (char *)&(info->uid), 4);

        memcpy((char *)&data[SVC_NAME_FIELD], info->svc, 6);
        return info->len;
}

int remote_closed(int index, int type)
{
        return 1;
}

int prepare_shutdown(int code)
{
        return 1;
}
int outmsg_recovery(char *data, msg_info_t *info)
{
        return -1;
}

int inmsg_recovery(char *data, msg_info_t *info)
{
        return -1;
}
<Makefile>

Refer to the makefile example in Synchronous INBOUND X25GW.

3.3. Server

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

SENDSVC(TPSVCINFO *msg)
{
    char *sndbuf;
    long len;

    printf("[%s] Service Started!\n", msg->name);

    len = (msg->len);
    printf("len is [%d]\n", len);

    if ((sndbuf=(char *)tpalloc("CARRAY", NULL, len)) == NULL) {
        printf("sndbuf alloc failed !\n");
        tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    memcpy(sndbuf, msg->data, msg->len);
    /* Call X25GW service via tpforward. */
    tpforward("X25GW", (char *)sndbuf, len, TPNOREPLY);
}
<rcvsvr.c>
#include <stdio.h>
#include <string.h>
#include <usrinc/atmi.h>

/* X25GW dynamically calls tprelay to this service. */
RECVSVC(TPSVCINFO *msg)
{
    char *rcvbuf;
    long len;

    printf("[%s] Service Started!\n", msg->name);

    len = msg->len;
    rcvbuf = msg->data;

    if(tpurcode != 0) {
        printf( "tpurcode is [%d] tperrmsg is [%s]\n", tpurcode,
                 tpstrerror(tpurcode) );
        tpreturn( TPFAIL, -1,(char *)rcvbuf, len, 0 );
    }
    tpreturn( TPSUCCESS, 0,(char *)rcvbuf, len, TPNOFLAGS );
}

3.4. Client

<cli_x25gw.c>
/* The following example program uses a Tmax client to call SENDSVC.
   Before calling SENDSVC, configure the service to which X25GW
   will tprelay to in the message header. */

#include <stdio.h>
#include <usrinc/atmi.h>
#include "../server/custom.h"

int main(int argc, char **argv)
{
    int ret;
    msg_body_t *body;
    char *buf;
    long rlen;

    ret = tmaxreadenv("tmax.env", "TMAX");
    if (ret < 0) {
        printf("tmaxreadenv fail...[%s]\n", tpstrerror(tperrno));
    }

    ret = tpstart((TPSTART_T *)NULL);
    if (ret < 0) {
        printf("tpstart fail...[%s]\n", tpstrerror(tperrno));
        return 0;
    }
    buf = tpalloc("STRING", 0, 0);
    if (buf == NULL){
        printf("buf tpalloc fail..[%s]\n", tpstrerror(tperrno));
        tpend();
        return 0;
    }

    body = (msg_body_t *)buf;
    memcpy(body->data, argv[1], strlen(argv[1]));
    body->data[51] = 0;
    memcpy(body->retsvcname, "RECVSVC", 9);
    body->retsvcname[8] = 0;
    memcpy(buf, (char *)body, sizeof(msg_body_t));

    /* Call the service that will call X25GW. */
    ret=tpcall("SENDSVC",buf,sizeof(msg_body_t), &buf, &rlen, 0);
    if (ret < 0){
        printf("tpcall fail...[%s]\n", tpstrerror(tperrno));
        tpfree((char *)buf);
        tpend();
        return 0;
    }

    body = (msg_body_t *)buf;

    printf("return value = %s\n", body->data);
    tpfree((char *)buf);
    tpend();
}