Example: Using the user-defined communications programs for file transfer

This example shows how X.25-oriented applications use the user-defined communications support to connect to remote systems. Two user-defined application programs, written in ILE C, are used to illustrate a simple file transfer between systems over an X.25 packet-switching data network (PSDN).

Although an X.25 example is shown, many of the same concepts can be applied to applications running over token-ring and Ethernet local area networks (LANs). For the purposes of the examples, the APIs are referred to by their call names. The includes header, hexconv, and typedefs are not in QSYSINC. These includes are only documented in the examples.

For this example, the following network configuration will be used.

Example X.25 network

X.25 overview

In this example X.25 network, the source application on System A is responsible for establishing a switched virtual circuit, or connection to the target application running on System B. This is done by using the remote network address (System B's address) of X'0000652'. When the target application on System B is initialized, it waits for notification of an incoming call packet before proceeding. Once the virtual circuit is established, the source application reads records from a file into its output buffer and sends them to the target application using normal X.25 data transfer procedures. While receiving the file data, the target application writes the data to a local file on System B. When the file transfer completes, the source application closes the connection by issuing an X.25 clear request packet and ends. When receiving the clear indication packet, the target application also ends.

User-defined communications support overview

Both the source and target applications call the Query Line Description (QOLQLIND) API to obtain information about the local X.25 line being used. This information is stored in a local control block for use in establishing the peer connection during X.25 connection processing. Both applications also call the Enable Link (QOLELINK) API to enable the link for future communications. The line name, communications handle, and remote DTE address are passed to both programs as arguments to the C function main(). For simplicity, the user space names and data queue name on the call to the QOLELINK API are coded directly in the applications.

Note: Keyed data queue support is used by both applications. The key length is 3 and the keys used are source (SRC) and target (TGT) for the source and target applications, respectively.

Activating filters

Once the links have been enabled and both applications have read their respective enable-complete entries from their data queues, the target application program calls the Set Filter (QOLSETF) API to activate a filter. The filter activated then identifies the protocol of the local X.25 service user. This filter is used by the user-defined communications support on System B to route incoming calls. The actual filter type activated is X'00' (for X.25 PID) and its associated value is X'21'. For more information concerning filters, see Set Filter (QOLSETF) API. After activating the X'21' filter, the target application waits for the source application to request a connection.

Establishing a connection

The source application calls the Send Data (QOLSEND) API with a X'B000' operation in its output data buffer to establish a switched virtual circuit (SVC) to the target application. Included in the first byte of the call user data is the protocol ID of the target application, or X'21'. When the user-defined communications support on System B sees the incoming call packet with the first byte of user data equal to a previously activated filter, the call is routed to the process responsible for activating that filter. In this case, the target application will receive notification of an incoming call since it previously activated filter X'21'.

While waiting for the incoming call, the target application calls the Receive Data (QOLRECV) API to receive a X'B201' operation with incoming call data. After doing so, the target application accepts the X.25 connection by calling the QOLSEND API with a X'B400' operation in its output data buffer.

Sending data

Once the peer connection is established between the source and target applications running on System A and System B respectively, the file transfer takes place. The source application reads records from a local file and calls the QOLSEND API with X'0000' operations in its output data buffer to transfer the file data to System B. This process continues until the entire contents of the source file has been sent to System B.

Receiving data

After accepting the X.25 connection, the target application waits until its data queue receives incoming-data entries. When the first entry is read from the queue, the QOLRECV API is called to determine which operation was received. Barring failure, the target application should receive a X'0001' operation as a result of the QOLRECV API call. The data contained in the input data buffer is the file data received from System A. While receiving the file data, the target application writes the data to a local file. This process continues until the entire contents of the file is received from System A. The target application then assumes the file transfer is complete when an operation other than a X'0001' operation is received after a successful call to the QOLRECV API. Most likely, the first non-X'0001' operation received will be X'B301' operation, signalling that the user-defined communications support running on System B received an SVC clear indication.

Clearing the connection and disabling links

Once the entire contents of the file has been read and sent to System B, the source application calls the QOLSEND API with a X'B100' operation in its output data buffer to clear the X.25 connection. Afterwards, the source application closes its local file, disables its local link by calling the QOLDLINK API, and ends.

When the source application program sends a X'B100' operation, it causes the target application to receive a X'B301' operation. After receiving this operation, the target application program calls the QOLSEND API with a X'B100' operation to locally close the connection between itself and the user-defined communications support. Afterwards, the target application closes its local file, disables its local link by calling the QOLDLINK API, and ends.

Using timers and the data queue support

Both the source and target application programs use the user-defined communications support timer service to manage the reception of certain operations. This is done by setting a timer before checking the data queue for an entry. For example, the target application sets a timer to manage the reception of file data from the source application. If the timer expires, the user-defined communications support places a timer-expired entry on the application's data queue. The target application then assumes when receiving this entry that the source application ended abnormally. The target application can then take the appropriate action to end itself.

ILE C compiler listings

Below are the listings for the source and target applications described in the previous paragraphs. Note the reference numbers (for example, (1)) in the listings. Detailed explanations of each reference number block are found in Source application program listing references and Target application program listing references.

The target application compiler listing can be found in Target application on System B listing.

Note: By using the code examples, you agree to the terms of the Code license and disclaimer information.

Source application on System A listing

In this example, the source application is the initiator of all meaningful work. In summary, the source program listed on the following pages does the following:

  • Calls the QOLQLIND API to get local X.25 line information
  • Opens the local file
  • Calls the QOLELINK API to establish a link for communications
  • Calls the QOLSEND API with X'B000' operation to establish a peer (SVC) connection
  • Sends the local file to the target system using X'0000' operations
  • Calls the QOLSEND API with X'B100' operation to clear the peer (SVC) connection
  • Calls the QOLDLINK API to disable the link
  • Calls the QOLTIMER API to manage the reception of data queue entries

To create the program using ILE C, use the Create Bound C (CRTBNDC) command.


 Program name  . . . . . . . . . :
(SOURCE)
   Library name  . . . . . . . . :      UDCS_APPLS
 Source file . . . . . . . . . . :   QCSRC
   Library name  . . . . . . . . :      UDCS_APPLS
 Source member name  . . . . . . :   SOURCE
 Text Description  . . . . . . . :   Source Application Example
 Output  . . . . . . . . . . . . :   *NONE
 Compiler options  . . . . . . . :   *SOURCE  *NOXREF  *SHOWUSR
                                 :   *SHOWSYS  *NOSHOWSKP  *NOEXPMAC
                                 :   *NOAGR *NOPPONLY  *NODEBUG
                                 :   *GEN *NOSECLVL  *PRINT  *LOGMSG
                                 :   *USRINCPATH
 Checkout Options  . . . . . . . :   *NOAGR
 Optimization  . . . . . . . . . :   *NONE
 Inline Options:
   Inliner . . . . . . . . . . . :   *OFF
   Mode  . . . . . . . . . . . . :   *NOAUTO
   Threshold . . . . . . . . . . :   250
   Limit . . . . . . . . . . . . :   2000
 Debugging View  . . . . . . . . :   *NONE
 Define Names  . . . . . . . . . :   *NONE
 Language Level  . . . . . . . . :   *SOURCE
 Source Margins:
   Left margin . . . . . . . . . :   1
   Right margin  . . . . . . . . :   32754
 Sequence columns:
   Left column . . . . . . . . . :   *NONE
   Right column  . . . . . . . . :
 Message flagging level  . . . . :   0
 Compiler messages:
   Message limit . . . . . . . . :   *NOMAX
   Message limit severity  . . . :   30
 Replace Program Object  . . . . :   *YES
 User Profile  . . . . . . . . . :   *USER
 Authority . . . . . . . . . . . :   *LIBCRTAUT
 Target Release  . . . . . . . . :   *CURRENT
 System includes . . . . . . . . :   *YES

/*******************************************************************/
/**  Program Name: Source Application Program Example             **/
/**                                                               **/
/**                                                               **/
/**  Function:                                                    **/
/**  This is the source application program example that uses     **/
/**  X.25 services provided by the user-defined communications    **/
/**  support to transfer a simple file to the target application  **/
/**  program running on system B.  This program performs the      **/
/**  following:                                                   **/
/**     01.  Open the source file name INFILE.                    **/
/**     02.  Call QOLQLIND API to obtain local line information.  **/
/**     03.  Enable a link.                                       **/
/**     04.  Send a 'B000'X operation (call request).             **/
/**     05.  Receive a 'B001'X operation (call confirmation).     **/
/**     06.  Read record(s) from the file opened in step 1). and  **/
/**          send '0001'X operation(s) to transfer the file to    **/
/**          the target application program.                      **/
/**     07.  Send a 'B100'X operation (clear call request).       **/
/**     08.  Receive a 'B101'X operation.                         **/
/**     09.  Disable the link enabled in step 3).                 **/
/**                                                               **/
/**  A data queue will be actively used to manage the operation   **/
/**  of this program.  Data queue support will be used to monitor **/
/**  for the completion of the enable and disable routines, as    **/
/**  well as timer expirations and incoming data.  Timers are     **/
/**  used to ensure that there will never be an infinite wait on  **/
/**  the data queue.  If a timer expires, the link enabled will   **/
/**  be disabled and the program will stop.                       **/
/**                                                               **/
/** Inputs:                                                       **/
/** The program expects the following input parameters            **/
/**     Line Name:   This is the name of the line description     **/
/**                  that will be used to call the QOLELINK API.  **/
/**                  The line must be an X.25 line with at least  **/
/**                  one SVC of type *SVCBOTH or *SVCOUT.         **/
/**                                                               **/
/**     CommHandle:  This is the logical name that will be used   **/
/**                  to identify the link enabled.                **/
/**                                                               **/
/**     Remote DTE Address:  The is the Local Network Address     **/
/**                          of System B.                         **/
/**                                                               **/
/**                                                               **/
/** Outputs:                                                      **/
/** Current status of the file transfer will be provided when     **/
/** running this program.  If an error should occur, then a       **/
/** message will be displayed indicating where the error occurred **/
/** and the program will end.        If the program completes     **/
/** successfully, a "successful completion" message will be       **/
/** posted.                                                       **/
/**                                                               **/
/** Language:  ILE C                                              **/
/**                                                               **/
/** APIs used: QOLELINK, QUSPTRUS, QOLRECV, QOLSEND, QOLDLINK,    **/
/**            QOLTIMER, QRCVDTAQ                                 **/
/**                                                               **/
/*******************************************************************/
/*******************************************************************/
/*******************************************************************/
#include "header"
#include "typedef"
#include "hexconv"
(1)

/************   Typedef  Declarations      *******************/
(2)

void senddata(sendparms *a, char *b, desc *c, char *d, char *e, int f);
void sndformat1(sendparms *a,char *b, char *c, char *d, qlindparms *f);
void sndformat2 (sendparms *a, char *b, char *c);
void setfilters (hdrparms *a);
void byte (char *a, int b, char *c, int d);
void printespec (espec *a);
void settimer(unsigned short *a,char *b,qentry *c,usrspace *d,char *e);
void dequeue (int a, char *b, qentry *c, usrspace *d);
void x25lind (qlindparms *a, char *b);
int getline (char *a, int b, FILE *c);
void disablelink (disableparms *a, char *b, usrspace *c);
void handler (disableparms a, usrspace *b);

void _GetExcData(_INTRPT_Hndlr_Parms_T *parms);
/**********************************************************/
/***************   Start Main Program   *******************/
/**********************************************************/
main (int argc, char *argv[])
{
/************  Variable  Declarations      *******************/
  usrspace inbuff,       /*  Input Data Buffer */
           indesc,       /*  Input Buffer Descriptor  */
           outbuff,      /*  Output Data Buffer  */
           outdesc,      /*  Output Buffer Descriptor */
           qname;        /*  Data Queue  */
  int length,                  /* Data Queue key length  */
      linesiz,                 /* Length of line that is read in */
      i= 0;                    /* counter  */
  unsigned short expctid;      /*  Message ID that is expected */
  char commhandle[10],     /*  Command Line Parameter  */
       *buffer,                /* Pointer to buffer     */
       rmtdte[18],         /* Remote DTE read in       */
       line[132],          /* Line to read in       */
       key[256];           /* Data Queue key identifier  */
  desc *descriptor;            /* Pointer to buffer descriptor  */
  /** definitions for the API functions  **/
  enableparms enable;
  disableparms disable;
  sendparms send;
  recvparms recv;
  setfparms setf;
  timerparms timer;
  qlindparms qlind;
  qentry dataq;
  hdrparms *header;

(3)

  /***---  Open the file to send to remote side        ----**/
  if ((fptr = fopen("UDCS_APPLS/INFILE(INFILE)", "r")) == NULL)
     {
     printf("Unable to open source input file in UDCS_APPLS LIB.\n");
     printf("The Program was terminated.\n\n");
     return;
     }
  /***---  Open the display file as our input screen.  ----**/
  if ((screen = fopen("ERRORSPEC", "ab+ type=record")) == NULL)
     {
     printf("Unable to open display file.\n");
     printf("The Program was terminated.\n\n");
     return;
     }
  /** set the exception handler  **/

  signal(SIGALL,SIG_DFL);
  /**  Clear the command line Parameters  **/
  strncpy(enable.linename, "          ", 10);  /* Clear linename */
  strncpy(commhandle, "          ", 10);       /* Clear Commhandle*/
  strncpy(rmtdte, "                 ", 17);    /* Clear Remote DTE*/
  /**  Receive command line Parameters  **/
  strncpy(enable.linename, argv[1], strlen(argv[1]));
  strncpy(commhandle, argv[2], strlen(argv[2]));
  strncpy(rmtdte, argv[3], strlen(argv[3]));
  rmtdte[strlen(argv[3])] = '\0';
  /** Initialize the user spaces  **/
  strncpy(inbuff.library, "UDCS_APPLS", 10);    /*  Input Buffer */
  strncpy(inbuff.name, "SOURCEIBUF", 10);
  strncpy(indesc.library, "UDCS_APPLS", 10);    /* Input B Desc  */
  strncpy(indesc.name, "SOURCEBDSC", 10);
  strncpy(outbuff.library, "UDCS_APPLS", 10);   /*  Output Buffer*/
  strncpy(outbuff.name, "SOURCEOBUF", 10);
  strncpy(outdesc.library, "UDCS_APPLS", 10);   /* Output B Desc */
  strncpy(outdesc.name, "SOURCEODSC", 10);
  strncpy(qname.library, "UDCS_APPLS", 10);   /*  Data queue  */
  strncpy(qname.name, "X25DTAQ   ", 10);
  /*****   retrieve the line description information  ******/
  x25lind (&qlind, enable.linename);
  if ((qlind.retcode != 0) || (qlind.reason != 0))
     {
     printf("Query line description failed.\n");
     printf("Return code = %d\n", qlind.retcode);
     printf("Reason code = %d\n\n", qlind.reason);
     return;
     }
  /*****   Hard Code the QOLELINK Input Parameters  ******/
  enable.maxdtax25 = 512;
  enable.keylength = 3;
  strncpy (enable.keyvalue, "SND", 3);

(4)

  /**************************************************/
  /************  Enable the line  *******************/
  /**************************************************/
  QOLELINK (&(enable.retcode), &(enable.reason), &(enable.tdusize),\
      &(enable.numunits), &(enable.maxdtalan), &(enable.maxdtax25),\
      (char *)&inbuff, (char *)&indesc, (char *)&outbuff,\
      (char *)&outdesc, &(enable.keylength), enable.keyvalue,\
      (char *)&qname, enable.linename, commhandle);
  if ((enable.retcode != 0) || (enable.reason != 0))
     {
     printf("Line %.10s with Commhandle %.10s was NOT ENABLED.\n",\
             enable.linename, commhandle);
     printf("Return code = %d\n", enable.retcode);
     printf("Reason code = %d\n\n", enable.reason);
     return;
     }

(5)

  /*-------- Set a timer for Enable Link  ---------**/
  expctid = 0xF0F0;
  settimer(&expctid, "Enable", &dataq, &qname, commhandle);
  if (expctid != 0xF0F0)
     {
     disablelink (&disable, commhandle, &qname);
     return;
     }

(6)

  /******************************************************************/
  /**************   Set up a Call Request Packet  *******************/
  /******************************************************************/
  /****   Get pointers to the user spaces.   ******/
  QUSPTRUS(&outbuff, &buffer);
  QUSPTRUS(&outdesc, &descriptor);
  send.ucep = 26;              /*  set the UCEP number  */
  send.operation = 0xB000;     /*  send a call request  */
  send.numdtaelmnts = 1;       /*  send one data unit   */
  /**-----------    Send the packet   ---------**/
  sndformat1 (&send, buffer, rmtdte, commhandle, &qlind);
  if ((send.retcode != 0) || (send.reason != 0))
     {
     printf("Call request packet not sent\n");
     printf("Return code = %d\n", send.retcode);
     printf("Reason code = %d\n", send.reason);
     printf("new pcep %d\n", send.newpcep);
     printespec(&(send.errorspecific));
     disablelink (&disable, commhandle, &qname);
     return;
     }

(7)

  /*****************************************************************/
  /***********   Receive the Call CONFIRMATION packet       ********/
  /*****************************************************************/
  /*-------- Set a timer to receive a message  ---------**/
  expctid = 0xF0F3;
  settimer(&expctid, "Rcv Call", &dataq, &qname, commhandle);
  if (expctid != 0xF0F3)
     {
     disablelink (&disable, commhandle, &qname);
     return;
     }
  QOLRECV (&(recv.retcode), &(recv.reason), &(recv.ucep),\
           &(recv.pcep), &(recv.operation), &(recv.numdtaunits),\
           &(recv.dataavail), &(recv.errorspecific), commhandle);
  if ((recv.retcode != 0) || (recv.reason != 0))
     {
     printf("Recv Call reqst resp failed\n");
     printf("return code %d\n", recv.retcode);
     printf("reason code %d\n", recv.reason);
     printespec(&(send.errorspecific));
     disablelink (&disable, commhandle, &qname);
     return;
     }
  /* Interpret the Received Operation  */
  if (recv.operation != 0xB001)
     {
     printf("Recvd opr %x instead of opr B001\n", recv.operation);
     disablelink (&disable, commhandle, &qname);
     return;
     }
  printf("We have an X.25 SVC connection\n\n");

(8)

  /*****************************************************************/
  /***************   Send the file to the target application *******/
  /*****************************************************************/
  send.pcep = send.newpcep;       /*  set the PCEP number  */
  /***************   Send the Mbr LGRF in file DOC  ****************/
  linesiz = getline(line, 92, fptr);   /* Get first record  **/
  while (linesiz != 0)
     {
     /***************   Send a Packet of Data    ***************/
     /****   Get pointers to the user spaces.   ******/
     QUSPTRUS(&outbuff, &buffer);
     QUSPTRUS(&outdesc, &descriptor);
     send.operation = 0x0000;
     send.numdtaelmnts = 1;
     /**-----   Send the packet     -------------**/
     senddata (&send, buffer, descriptor, commhandle, line, linesiz);
     if ((send.retcode != 0) || (send.reason != 0))
        {
        printf("Data NOT sent for commhandle %.9s\n", commhandle);
        printf("Return code = %d\n", send.retcode);
        printf("Reason code = %d\n", send.reason);
        printf("new pcep %d\n", send.newpcep);
        printespec(&(send.errorspecific));
        disablelink (&disable, commhandle, &qname);
        return;
        }
     i = i + 1;
     printf("Data %d Sent for commhandle %.9s.\n\n", i, commhandle);
     linesiz = getline(line, 92, fptr);  /** Get next record  **/
     }                        /***  End While loop  ***/
  /***************************************************************/
  /***************   Set up a Clear Request Packet  **************/
  /***************************************************************/
(9)

  /****   Get pointers to the user spaces.   ******/
  QUSPTRUS(&outbuff, &buffer);
  QUSPTRUS(&outdesc, &descriptor);
  send.operation = 0xB100;         /**  send clear request  **/
  send.numdtaelmnts = 1;           /**  send one data unit  **/
  /**-----------    Send the packet   ---------**/
  sndformat2 (&send, buffer, commhandle);
  if ((send.retcode != 0) || (send.reason != 0))
     {
     printf("Clear request packet not sent\n");
     printf("Return code = %d\n", send.retcode);
     printf("Reason code = %d\n", send.reason);
     printf("new pcep %d\n", send.newpcep);
     printespec(&(send.errorspecific));
     disablelink (&disable, commhandle, &qname);
     return;
     }

(10)
  /***************************************************************/
  /***********   Receive the Clear Request Response packet   *****/
  /***************************************************************/
  /*-------- Set a timer to receive a message  ---------**/
  expctid = 0xF0F3;
  settimer(&expctid, "Rv Clr Rqt", &dataq, &qname, commhandle);
  if (expctid != 0xF0F3)
     {
     disablelink (&disable, commhandle, &qname);
     return;
     }
  /***********   Call QOLRECV to Receive the Clear Response  *****/
  /****   Get pointers to the user spaces.   ******/
  QUSPTRUS (&inbuff, &buffer);
  QUSPTRUS (&indesc, &descriptor);
  QOLRECV (&(recv.retcode), &(recv.reason), &(recv.ucep),\
           &(recv.pcep), &(recv.operation), &(recv.numdtaunits),\
           &(recv.dataavail), &(recv.errorspecific), commhandle);
  if ((recv.retcode != 0) || (recv.reason != 0))
     {
     printf("Recv clear response failed\n");
     printf("return code %d\n", recv.retcode);
     printf("reason code %d\n", recv.reason);
     printespec(&(send.errorspecific));
     disablelink (&disable, commhandle, &qname);
     return;
     }
  /* Interpret the Received Operation  */
  if (recv.operation != 0xB101)
     {
     printf("Recvd opr %x instead of opr B101\n", recv.operation);
     disablelink (&disable, commhandle, &qname);
     return;
     }
  /***********************************************/
  /***  Disable the link and end the program  ****/
  /***********************************************/
  disablelink (&disable, commhandle, &qname);
  printf("******   SOURCE completed successfully   ******\n\n");
}    /* End Main */
/*****************************************************************/
/************** Start Subroutine Section    **********************/
/*****************************************************************/
/*************************************************************/
/***************     Send a Packet of Data  ******************/
(11)

void senddata (sendparms *send,
              char *buffer,
              desc *descriptor,
              char *commhandle,
              char *line,
              int linesiz)
{
  descriptor->length = linesiz;
  descriptor->more = 0;
  descriptor->qualified = 0;
  descriptor->interrupt = 0;
  descriptor->dbit = 0;
  strncpy (buffer, line, linesiz);
  QOLSEND (&(send->retcode), &(send->reason),
&(send->errorspecific),\
           &(send->newpcep), &(send->ucep), &(send->pcep), \
           commhandle, &(send->operation), &(send->numdtaelmnts));
}  /* End senddata Subroutine  */
/*********************************************************/
/************  Routine to fill X.25 Format I   ***********/
void sndformat1 (sendparms *send,
                 char *buffer,
                 char *rmtdte,
                 char *commhandle,
                 qlindparms *qlind)
{
  format1 *output = (format1 *) buffer;
  register int counter;
  register querydata *qd;
  qd = (querydata *)&(qlind->userbuffer);
  output->type = 2;                 /*  SVC used  */
  output->logchanid = 0x0;
  output->sendpacksize = qd->x25data.defsend;
  output->sendwindsize = qd->x25data.windowsend;
  output->recvpacksize = qd->x25data.defrecv;
  output->recvwindsize = qd->x25data.windowrecv;
  output->dtelength = strlen(rmtdte);
  byte(output->dte, 16, rmtdte, strlen(rmtdte));
  output->dbit = 0;
  output->cug = 0;
  output->cugid = 0;
  output->reverse = 0;
  output->fast = 0;
  output->faclength = 0;
  byte(output->facilities, 109, "", 0);
  output->calllength = 1;
  byte(output->callud, 128, "21", 2);   /* Contains Remote PID */
  output->misc[0] = 0;     /* change to 0x80 for reset support */
  output->misc[1] = 0;
  output->misc[2] = 0;
  output->misc[3] = 0;
  output->maxasmsize = 16383;
  output->autoflow = 32;
  QOLSEND (&(send->retcode), &(send->reason),
&(send->errorspecific),\
           &(send->newpcep), &(send->ucep), &(send->pcep),\
           commhandle, &(send->operation), &(send->numdtaelmnts));
}  /* End sndformat1 Subroutine  */
/**********************************************************/
/************  Routine to fill X.25 Format II   ***********/
void sndformat2 (sendparms *send,
                 char *buffer,
                 char *commhandle)
{
  format2 *output = (format2 *) buffer;
  output->type = 1;
  output->cause = 'FF';
  output->diagnostic = 'FF';
  output->faclength = 0;
  byte(output->facilities, 109, "", 0);
  output->length = 0;
  byte(output->userdata, 128, "", 0);
  QOLSEND (&(send->retcode), &(send->reason),
&(send->errorspecific),\
           &(send->newpcep), &(send->ucep), &(send->pcep),\
           commhandle, &(send->operation), &(send->numdtaelmnts));
}  /* End sndformat2 Subroutine  */

(12)

/********************************************/
/********    Routine to disable   ***********/
void disablelink (disableparms *disable,
                  char *commhandle,
                  usrspace *qname)
{
unsigned short expctid;
qentry dataq;
  disable->vary = 1;     /*  Hard coded to be varied off  */
  QOLDLINK (&(disable->retcode), &(disable->reason),\
                        commhandle, &(disable->vary));
  if ((disable->retcode != 0) && (disable->reason != 00))
     {
     printf ("Link %.10s did not disabled.\n", commhandle);
     printf ("return code = %d\n", disable->retcode);
     printf ("reason code = %d\n\n", disable->reason);
     }
  /**-------  Set a timer to receive disable complete msg --------**/
  expctid = 0xF0F1;
  settimer(&expctid, "Disable", &dataq,  qname, commhandle);
  if (expctid != 0xF0F1)
     {
     printf("Disable link did not complete successfully");
     return;
     }
  printf ("%.10s link disabled \n", commhandle);
  /**  close the files  **/
  fclose(fptr);
  fclose(screen);
}  /* End disablelink Subroutine  */
/**************************************************************/
/**    Routine to convert string to Hexadecimal format   ******/
void byte (char *dest,
           int dlength,
           char *source,
           int slength)
{
  register int counter;
  char holder[2];
  for (counter=0;counter<dlength;counter++)
   dest[counter]=0;
  for (counter=slength-1;counter>=0;counter--)
   if (isxdigit(source[counter]))
     {
      holder[0]=source[counter];
      holder[1]='\0';
      if  (counter % 2 == 0)
        dest[counter/2] += (char) hextoint(holder)*16;
      else dest[counter/2] += (char) hextoint(holder);
     }
}  /* End byte Subroutine  */
/**************************************************************/
/**    Routine to display the ErrorSpecific output       ******/
void printespec(espec *errorspecific)
{
  especout outparms;

  sprintf(outparms.hwecode, "%.8X", errorspecific->hwecode);
  sprintf(outparms.timestamp, "%.8X%.8X", errorspecific->timestamphi,\
                                        errorspecific->timestamplo);
  sprintf(outparms.elogid, "%.8X", errorspecific->elogid);
  if (errorspecific->flags & 0x40)
    outparms.fail = 'Y';
  else outparms.fail = 'N';
  if (errorspecific->flags & 0x20)
    outparms.zerocodes = 'Y';
  else outparms.zerocodes = 'N';
  if (errorspecific->flags & 0x10)
    outparms.qsysopr = 'Y';
  else outparms.qsysopr = 'N';
  sprintf(outparms.cause,"%.2X", errorspecific->cause);
  sprintf(outparms.diagnostic, "%.2X", errorspecific->diagnostic);
  sprintf(outparms.erroffset, "%.6d", errorspecific->erroroffset);
  fwrite(&outparms, 1, sizeof(especout), screen);
  fread("", 0, 0, screen);
}  /* End printespec Subroutine  */
(13)

/*************   Set a timer and dequeue next entry  ******/
void settimer (unsigned short *expctid,
               char *process,
               qentry *dataq,
               usrspace *qname,
               char *commhandle)
{
timerparms timer;
disableparms disable;
int length;
char key[6];
  timer.interval = 20000;      /*  Set timer for 20 seconds */
  timer.establishcount = 1;
  timer.keylength = 3;          /*  Set key value  */
  strncpy(timer.keyvalue, "SRC", 3);
  timer.operation = 1;           /*  Set a timer  */
  QOLTIMER (&(timer.retcode), &(timer.reason), timer.handleout,\
           timer.handlein, (char *)qname, &(timer.operation),\
           &(timer.interval), &(timer.establishcount),\
           &(timer.keylength), timer.keyvalue, timer.userdata);
  if ((timer.retcode != 0) || (timer.reason != 0))
     {
     printf("%s timer failed while being set.\n", process);
     printf("Return code = %d\n", timer.retcode);
     printf("Reason code = %d\n\n", timer.reason);
     }
/**-------  Dequeue an entry  --------**/
  strncpy(key, "SRC",3);
  length = 3;
  dequeue (length, key, dataq, qname);
  /*** Cancel timer  ***/
  if (dataq->msgid != 0xF0F4)
     {
     strncpy(timer.handlein, timer.handleout, 8);
     timer.operation = 2;           /*  Set one timer  */
     QOLTIMER (&(timer.retcode), &(timer.reason), timer.handleout,\
           timer.handlein, (char *)qname, &(timer.operation),\
           &(timer.interval), &(timer.establishcount),\
           &(timer.keylength), timer.keyvalue, timer.userdata);
     if ((timer.retcode != 0) || (timer.reason != 0))
        {
        printf("%s timer failed while being canceled\n", process);
        printf("Return code = %d\n", timer.retcode);
        printf("Reason code = %d\n\n", timer.reason);
        }
     }
  if (dataq->msgid != *expctid)
      {
      printf ("A %.4X message ID was received instead of %.4X\n",\
              dataq->msgid, *expctid);
      printf ("%s completion message was not received\n", process);
      *expctid = dataq->msgid;
      }
}  /* End settimer Subroutine  */
/***************************************************************/
/*******   Dequeues the Incoming Message and processes it ******/
void dequeue (int length,
              char *key,
              qentry *dataq,
              usrspace *qname)
{
  char fldlen[3],
       waittime[3],
       keylen[2],
       senderid[2],
       *pointer,
       order[2];
  register int counter;
  waittime[0] = 0;
  waittime[1] = 0;
  waittime[2] = 0x1D;   /*  Hard code a delay of infinite   */
  keylen[0] = 0;
  keylen[1] = 0x3F;   /* Hard code a keylength of 3  */
  senderid[0] = 0;
  senderid[1] = 0x0F;
  strncpy(order, "EQ", 2);
  fflush(stdin);
  pointer = (char *)dataq;
  for (counter = 0; counter < 336; counter++)
    pointer[counter] = 0;
  strncpy (dataq->type, "       ", 7);
  while ((strncmp(dataq->type, "*USRDFN", 7) != 0) || (fldlen == 0))
     QRCVDTAQ(qname->name, qname->library, fldlen, dataq, waittime,\
           order, keylen, key, senderid,"");
}  /* End dequeue Subroutine  */
(14)

/************************************************************/
/**  x25lind:  Retrieve X.25 line description information  **/
void x25lind (qlindparms *qlind, char *linename)
{
register int counter;
  for(counter=0;counter<256;counter++)
     qlind->userbuffer[counter]=0;
  qlind->format = 0x01;
  QOLQLIND (&(qlind->retcode), &(qlind->reason), &(qlind->nbytes),\
            qlind->userbuffer, linename, &(qlind->format));
}  /*  End x25lind Subroutine  */
/*******************************************************/
/**  Getline:  Read a record into line and return length   **/
int getline (char *line, int max, FILE *fptr)
{
   if (fgets(line, max, fptr) == NULL)
      return 0;
   else
      return strlen(line);
}  /* End getline Subroutine  */
/***************************************************/

Source application program listing references

The following reference numbers and explanations correspond to the reference numbers in the source application's program listing.

(1) Some general C structure declarations used by both the source and target application programs.
(2) Function prototypes of the internal functions used in this program.
(3) Call the C library routines fopen() and signal() to open the source file and set up a signal handler to process IBM® i exceptions, respectively. An example of an exception would be accessing a data area with a NULL pointer. If an exception situation is encountered, SIG_DFL, the default handler, will be called in order for the program to end.
(4) Call the QOLQLIND API to retrieve local configuration information from the line description about what will be used for communications. Next, call the QOLELINK API to enable the line description using the line name and communications handle passed as input parameters to this program.
(5) Call the QOLTIMER API to time the completion of the enable link operation. If the timer expires before the enable-complete entry is posted on the this program's data queue, then this program will end.
(6) Call the QOLSEND API with a X'B000' operation to establish a connection to the target application program.
(7) Monitor the source program's data queue for the call confirmation. The source program will be notified of the call confirmation by call the QOLRECV API and receiving a X'B001' operation in the program's input buffer.
(8) This is the main send loop for the source program. The data from the source file is placed one line at a time in the output buffer and then the QOLSEND API is called to send one data unit of the file to System B. This process repeats until the contents of the entire file have been transmitted to the target application.
(9) Call the QOLSEND API with a X'B100' operation to clear the peer connection.
(10) The source program will check its data queue for a response to the clear packet sent to the target system. Once the response is received, the program will clean up, call the QOLDLINK API to disable the link previously enabled, and end.
(11) The following C functions illustrate the various user-defined communications support APIs.
(12) This procedure illustrates a call to the QOLDLINK API. The vary option is set to vary off the associated *USRDFN network device.
(13) The settimer() calls the QOLTIMER API requesting timers for 20000 milliseconds, or twenty seconds. After setting a timer, the settimer() will call the dequeue() to remove an entry from the program's data queue.
(14) The x25lind() illustrates calling the QOLQLIND API.

Target application on System B listing

The target application waits for the source application to initiate the file transfer. The following list summarizes the actions of the target application:

  • Calls the QOLQLIND API to get local X.25 line information
  • Opens the local file
  • Calls the QOLELINK API to establish a link for communications
  • Calls the QOLSETF API to activate an X.25 protocol ID filter
  • Calls the QOLRECV API to receive the X'B201' operation (incoming call)
  • Calls the QOLSEND API with a X'B400' operation to accept the SVC connection
  • Receives the file from the target system using X'0001' operations
  • Calls the QOLRECV API to receive the X'B301' (connection failure notification)
  • Call the QOLSEND API with 'B100' operation to locally close the SVC connection
  • Calls the QOLDLINK API to disable the link
  • Calls the QOLTIMER API to manage the reception of data queue entries

To create the program using ILE C, use the Create Bound C (CRTBNDC) command.

Explanations of the reference numbers in the listing can be found in Target application program listing references.


 Program name  . . . . . . . . . :
(TARGET)
   Library name  . . . . . . . . :      UDCS_APPLS
 Source file . . . . . . . . . . :   QCSRC
   Library name  . . . . . . . . :      UDCS_APPLS
 Source member name  . . . . . . :   TARGET
 Text Description  . . . . . . . :  Target Application Example
 Output  . . . . . . . . . . . . :   *NONE
 Compiler options  . . . . . . . :   *SOURCE  *NOXREF *NOSHOWUSR
                                 :   *NOSHOWSYS *NOSHOWSKP *NOEXPMAC
                                 :   *NOAGR *NOPPONLY *NODEBUG
                                 :   *GEN  *NOSECLVL  *PRINT  *LOGMSG
                                 :   *USRINCPATH
 Checkout Options  . . . . . . . :   *NOAGR
 Optimization  . . . . . . . . . :   *NONE
 Inline Options:
   Inliner . . . . . . . . . . . :   *OFF
   Mode  . . . . . . . . . . . . :   *NOAUTO
   Threshold . . . . . . . . . . :   250
   Limit . . . . . . . . . . . . :   2000
 Debugging View  . . . . . . . . :   *NONE
 Define Names  . . . . . . . . . :   *NONE
 Language Level  . . . . . . . . :   *SOURCE
 Source Margins:
   Left margin . . . . . . . . . :   1
   Right margin  . . . . . . . . :   32754
 Sequence columns:
   Left column . . . . . . . . . :   *NONE
   Right column  . . . . . . . . :
 Message flagging level  . . . . :   0
 Compiler messages:
   Message limit . . . . . . . . :   *NOMAX
   Message limit severity  . . . :   30
 Replace Program Object  . . . . :   *YES
 User Profile  . . . . . . . . . :   *USER
 Authority . . . . . . . . . . . :   *LIBCRTAUT
 Target Release  . . . . . . . . :   *CURRENT
 System includes . . . . . . . . :   *YES

/*******************************************************************/
/**                                                               **/
/**  Program Name: Target Application Program Example             **/
/**                                                               **/
/**                                                               **/
/**  Function:                                                    **/
/**  This is the target application program example that uses     **/
/**  X.25 services provided by the user-defined communications    **/
/**  support to receive a simple file from the source application **/
/**  program running on System A.  This program performs the      **/
/**  following:                                                   **/
/**     01.  Open the target file named OUTFILE.                  **/
/**     02.  Call QOLQLIND to obtain local line information.      **/
/**     03.  Enable a link.                                       **/
/**     04.  Set a Filter on the enabled link.                    **/
/**     05.  Receive a 'B101'X operation (incoming call).         **/
/**     06.  Send a 'B400'X operation (accept call).              **/
/**     07.  Receive '0001'X operation(s) (incoming data) from    **/
/**          the source application program and write it to the   **/
/**          file opened in step 1).                              **/
/**     08.  Receive a 'B301'X operation (clear call indication). **/
/**     09.  Send a 'B100'X operation to respond locally to the   **/
/**          clearing of the connection.                          **/
/**     10.  Disable the link enabled in step 3).                 **/
/**                                                               **/
/**  A data queue will be actively used to manage the operation   **/
/**  of this program.  Data queue support will be used to monitor **/
/**  for the completion of the enable and disable routines, as    **/
/**  well as timer expirations and incoming data.  Timers are     **/
/**  used to ensure that there will never be an infinite wait on  **/
/**  the data queue.  If a timer expires, the link enabled will   **/
/**  be disabled and the program will stop.                       **/
/**                                                               **/
/**                                                               **/
/** Inputs:                                                       **/
/** The program expects the following input parameters:           **/
/**      Line Name:   This is the name of the line description    **/
/**                   that will be used to call the QOLELINK API. **/
/**                   The line must be an X.25 line with at least **/
/**                   one SVC of type *SVCBOTH or *SVCIN.         **/
/**                                                               **/
/**      CommHandle:  This is the logical name that will be used  **/
/**                   to identify the link enabled.               **/
/**                                                               **/
/**      Remote DTE Address:  The is the Local Network Address    **/
/**                           of system A.                        **/
/**                                                               **/
/**                                                               **/
/** Outputs:                                                      **/
/** Current status of the file transfer will be provided when     **/
/** running this program. If an error should occur, then a        **/
/** message will be displayed indicating where the error occurred **/
/** and the program will end.        If the program completes     **/
/** successfully, a "successful completion" message will be       **/
/** posted.                                                       **/
/**                                                               **/
/** Language:  ILE C                                              **/
/**                                                               **/
/** APIs used: QOLELINK, QUSPTRUS, QOLRECV, QOLSEND, QOLDLINK,    **/
/**            QRCVDTAQ, QOLTIMER                                 **/
/**                                                               **/
/*******************************************************************/
/*******************************************************************/
/*******************************************************************/
/*******************************************************************/
#include "header"
#include "typedef"
#include "hexconv"
void senddata(sendparms *a, char *b, desc *c, char *d, char *e, int f);
void sndformat1(sendparms *a,char *b, char *c, char *d, qlindparms *e);
void sndformat2 (sendparms *a, char *b, char *c);
void setfilters (hdrparms *a);
void byte (char *a, int b, char *c, int d);
void printespec (espec *a);
void settimer(unsigned short *a,char *b,qentry *c,usrspace *d,char *e);
void dequeue (int a, char *b, qentry *c, usrspace *d);
void putdata (char *a, int b, FILE *c);
void x25lind (qlindparms *a, char *b);
void disablelink (disableparms *a, char *b, usrspace *c);
void handler (disableparms a, usrspace *b);

void _GetExcData(_INTRPT_Hndlr_Parms_T *parms);
/**********************************************************/
/***************   Start Main Program   *******************/
/**********************************************************/
main (int argc, char *argv[])
{
/************  Variable  Declarations      *******************/
  usrspace inbuff,       /*  Input Data Buffer */
           indesc,       /*  Input Buffer Descriptor  */
           outbuff,      /*  Output Data Buffer  */
           outdesc,      /*  Output Buffer Descriptor */
           qname;        /*  Data Queue  */
  int length,                /* Data Queue key length  */
      inc, i, j;                     /*  counters  */
  unsigned short expctid;      /*  Message ID that is expected */
  char commhandle[10],    /*  Command Line Parameter  */
       rmtdte[17],        /*  Remote DTE Address  */
       *buffer,               /* Pointer to buffer     */
       key[256];          /* Data Queue key identifier  */
  desc *descriptor;           /* Pointer to buffer descriptor  */
/**  definitions for API functions  **/
  enableparms enable;
  disableparms disable;
  sendparms send;
  recvparms recv;
  setfparms setf;
  timerparms timer;
  qlindparms qlind;
  qentry dataq;
  hdrparms *header;
  /******  Annndddddd....  they're off!!   ***********/
(1)

  /***---  Open the file to put the received data.     ----**/
  if ((fptr = fopen("UDCS_APPLS/OUTFILE))", "w")) == NULL)
    {
     printf("Unable to open target output file in UDCS_APPLS LIB.\n");
     printf("The Program was terminated.\n\n");
     return;
    }
  /***---  Open the display file for error handling.   ----**/
  if ((screen = fopen("ERRORSPEC", "ab+ type = record")) == NULL)
    {
     printf("Unable to open display file.\n");
     printf("The Program was terminated.\n\n");
     return;
    }
  /***---  Set the Exception Handler        ----**/

  signal(SIGALL,SIG_DFL);
  /**  Clear the command line parameters  **/
  strncpy(enable.linename, "          ", 10);    /* Clear linename */
  strncpy(commhandle, "          ", 10);     /* Clear Commhandle */
  strncpy(rmtdte, "                 ", 17);     /* Clear Remote DTE */
  /**  Receive command line Parameters  **/
  strncpy(enable.linename, argv[1], strlen(argv[1]));
  strncpy(commhandle, argv[2], strlen(argv[2]));
  strncpy(rmtdte, argv[3], strlen(argv[3]));
  rmtdte[strlen(argv[3])] = '\0';
  /**   Initialize the user spaces  **/
  strncpy(inbuff.library, "UDCS_APPLS", 10);    /*  Input Buffer */
  strncpy(inbuff.name, "TARGETIBUF", 10);
  strncpy(indesc.library, "UDCS_APPLS", 10);    /* Input B Desc  */
  strncpy(indesc.name, "TARGETIDSC", 10);
  strncpy(outbuff.library, "UDCS_APPLS", 10);   /*  Output Buffer*/
  strncpy(outbuff.name, "TARGETOBUF", 10);
  strncpy(outdesc.library, "UDCS_APPLS", 10);   /* Output B Desc */
  strncpy(outdesc.name, "TARGETODSC", 10);
  strncpy(qname.library, "UDCS_APPLS", 10);   /*  Data queue  */
  strncpy(qname.name, "X25DTAQ   ", 10);
  /*****   retrieve the line description information  ******/
  x25lind (&qlind, enable.linename);
  if ((qlind.retcode != 0) || (qlind.reason != 0))
     {
     printf("Query line description failed.\n");
     printf("Return code = %d\n", qlind.retcode);
     printf("Reason code = %d\n\n", qlind.reason);
     return;
     }
  /*****   Hard Code the QOLELINK Input Parameters  ******/
  enable.maxdtax25 = 512;
  enable.keylength = 3;
  strncpy(enable.keyvalue, "RCV", 3);
(2)

  /**-------  Enable the link  -----------**/
  QOLELINK (&(enable.retcode), &(enable.reason), &(enable.tdusize),\
      &(enable.numunits), &(enable.maxdtalan), &(enable.maxdtax25),\
      (char *)&inbuff, (char *)&indesc, (char *)&outbuff,\
      (char *)&outdesc, &(enable.keylength), enable.keyvalue,\
      (char *)&qname, enable.linename, commhandle);
  if ((enable.retcode != 0) || (enable.reason != 0))
     {
     printf("Line %.10s with Commhandle %.10s was NOT ENABLED.\n",\
             enable.linename, commhandle);
     printf("Return code = %d\n", enable.retcode);
     printf("Reason code = %d\n\n", enable.reason);
     return;
     }
(3)

  /**-------  Set a timer for Enable link  --------**/
  expctid = 0xF0F0;
  settimer(&expctid, "Enable", &dataq, &qname, commhandle);
  if (expctid != 0xF0F0)
     {
     disablelink (&disable, commhandle, &qname);
     return;
     }
  /*************************************************************/
  /*******----  Set a Filter for the Link  --------*********/
  /*************************************************************/
(4)

  QUSPTRUS(&outbuff, &header);   /*  get the output buffer pointer */
  header->function = 1;          /*  add a filter   */
  header->type = 0;              /*  X.25 PID only  */
  header->number = 1;            /*  set 1 filter   */
  header->length = 16;           /*  X.25 filter length  */
  setfilters(header);            /*  Fill in the filter format */
  /*******----  Set the filter for the Link  --------*********/
  QOLSETF (&(setf.retcode), &(setf.reason), &(setf.erroffset),\
                commhandle);
  if ((setf.retcode != 0) || (setf.reason != 0))
     {
     printf("Set Filters Return Code = %.2d\n", setf.retcode);
     printf("Set Filters Reason Codes = %.4d\n", setf.reason);
     printf("Set Filters Error Offset = %.4d\n", setf.erroffset);
     return;
     }
  /*************************************************************/
  /****  Receive the incoming call packet and accept the call **/
  /*************************************************************/
  /**-------  Set a timer to receive data  --------**/
  expctid = 0xF0F3;
  settimer(&expctid, "Inc Call ", &dataq, &qname, commhandle);
  if (expctid != 0xF0F3)
     {
     disablelink (&disable, commhandle, &qname);
     return;
     }
(5)

  /**********  Receive the Incoming Data     **********/
  QUSPTRUS (&inbuff, &buffer);
  QUSPTRUS (&indesc, &descriptor);
  QOLRECV (&(recv.retcode), &(recv.reason), &(recv.ucep),\
           &(recv.pcep), &(recv.operation), &(recv.numdtaunits),\
           &(recv.dataavail), &(recv.errorspecific), commhandle);
  if ((recv.retcode != 0) || (recv.reason != 0))
     {
     printf("Recv incoming call packet failed\n");
     printf("return code %d\n", recv.retcode);
     printf("reason code %d\n", recv.reason);
     printespec(&(send.errorspecific));
     disablelink (&disable, commhandle, &qname);
     return;
     }
  /*** Interpret the Received Operation  ***/
  if (recv.operation != 0xB201)
     {
     printf("Recvd operation %x instead of B201", recv.operation);
     disablelink (&disable, commhandle, &qname);
     return;         /****  End the program  ***/
     }
(6)

  /******************************************************************/
  /** Send a response to accept the call and establish a connection */
  /******************************************************************/
  /****   Get pointers to the user spaces.   ******/
  QUSPTRUS(&outbuff, &buffer);
  QUSPTRUS(&outdesc, &descriptor);
  /*******   Set up Send Packet    *********/
  send.ucep = 62;                   /*  set UCEP to be 62  */
  send.pcep = recv.pcep;            /*  get the PCEP number  */
  send.operation = 0xB400;          /*  send a call request response*/
  send.numdtaelmnts = 1;            /*  send one data unit   */
  /**-----   Send the packet   ----------------**/
  sndformat1 (&send, buffer, rmtdte, commhandle, &qlind);
  if ((send.retcode != 0) || (send.reason != 0))
     {
     printf("Data NOT sent for commhandle %.9s\n", commhandle);
     printf("Return code = %d\n", send.retcode);
     printf("Reason code = %d\n", send.reason);
     printf("new pcep %d\n\n", send.newpcep);
     printespec(&(send.errorspecific));
     disablelink (&disable, commhandle, &qname);
     return;
     }
  printf("An X.25 SVC connection was completed\n\n");

(7)

  /**********************************************************/
  /****    Receive Incoming Data    *************************/
  /**********************************************************/
  /**-------  Set a timer to receive data  --------**/
  expctid = 0xF0F3;
  settimer(&expctid, "Inc Data ", &dataq, &qname, commhandle);
  if (expctid != 0xF0F3)
     {
     disablelink (&disable, commhandle, &qname);
     return;
     }
  /*******---  Receive the Incoming Data     ----******/
  /** Get pointer to user space  **/
  QUSPTRUS (&inbuff, &buffer);
  QUSPTRUS (&indesc, &descriptor);
  /**  Receive the data   **/
  QOLRECV (&(recv.retcode), &(recv.reason), &(recv.ucep),\
         &(recv.pcep), &(recv.operation), &(recv.numdtaunits),\
         &(recv.dataavail), &(recv.errorspecific), commhandle);
  if ((recv.retcode != 0) || (recv.reason != 0))
     {
     printf("Recv op for first data unit failed\n");
     printf("return code %d\n", recv.retcode);
     printf("reason code %d\n", recv.reason);
     printespec(&(send.errorspecific));
     disablelink (&disable, commhandle, &qname);
     return;
     }

(8)

  /**************************************************************/
  /*******   Start a loop to read in all the incoming data    ***/
  /**************************************************************/
  i = 1;
  while (recv.operation == 0x0001)
     {
     printf("%d Data Recvd {%.4x}.\n\n", i++, recv.operation);
     /**  Store all the data units in the file  **/
     for (j = 1; j <= recv.numdtaunits; j++) {
        putdata (buffer + (j - 1)*enable.tdusize,\
                 descriptor->length, fptr);
        descriptor = (desc *)((char *)descriptor + sizeof(desc));
        }   /* for */
     /**-------  Set a timer to wait for more data  -------**/
     if (recv.dataavail == 0)
        {
        /** Set timer  **/
        expctid = 0xF0F3;
        settimer(&expctid, "Wt Inc Dta", &dataq, &qname, commhandle);
        if (expctid != 0xF0F3)
           {
           disablelink (&disable, commhandle, &qname);
           return;
           }
        }
     /** Get pointer to user space  **/
     QUSPTRUS (&inbuff, &buffer);
     QUSPTRUS (&indesc, &descriptor);
     /**  Receive the data   **/
     QOLRECV (&(recv.retcode), &(recv.reason), &(recv.ucep),\
             &(recv.pcep), &(recv.operation), &(recv.numdtaunits),\
             &(recv.dataavail), &(recv.errorspecific), commhandle);
     }    /**  End  Receive data  while loop   ******/

(9)

  /****************************************************/
  /*********** Receive the Clear indication ***********/
  /****************************************************/
  if ((recv.retcode != 83) || (recv.reason != 4002))
     {
     printf("Recv opr for clear request failed\n");
     printf("return code %d\n", recv.retcode);
     printf("reason code %d\n", recv.reason);
     printespec(&(send.errorspecific));
     disablelink (&disable, commhandle, &qname);
     return;
     }
  /* Interpret the Received Operation  */
  if (recv.operation != 0xB301)
     {
     printf("Recvd operation %x instead of B301", recv.operation);
     disablelink (&disable, commhandle, &qname);
     return;         /****  end the program  ***/
     }

(10)

  /****************************************************************/
  /***********  Send local response to clear indication ***********/
  /****************************************************************/
  /****   Get pointers to the user spaces.   ******/
  QUSPTRUS(&outbuff, &buffer);
  QUSPTRUS(&outdesc, &descriptor);
  /*******   Set up the packet         ****************/
  send.operation = 0xB100;       /*  send a clear request packet  */
  send.numdtaelmnts = 1;         /*  send one data unit  */
  /**-----   Send the packet   ----------------**/
  sndformat2 (&send, buffer, commhandle);
  if ((send.retcode != 0) && (send.reason != 0))
     {
     printf("Response not sent for clear connection\n");
     printf("Return code = %d\n", send.retcode);
     printf("Reason code = %d\n", send.reason);
     printf("new pcep %d\n\n", send.newpcep);
     printespec(&(send.errorspecific));
     disablelink (&disable, commhandle, &qname);
     return;
     }
  /******************************************************/
  /*********** Receive the Clear Confirmation  **********/
  /******************************************************/
  /**-------  Set a timer to receive data  --------**/
  expctid = 0xF0F3;
  settimer(&expctid, "Clr Cnfrm", &dataq, &qname, commhandle);
  if (expctid != 0xF0F3)
     {
     disablelink (&disable, commhandle, &qname);
     return;
     }
  if ((recv.retcode != 00) || (recv.reason != 0000))
     {
     printf("Recv failed for clear confirmation\n");
     printf("return code %d\n", recv.retcode);
     printf("reason code %d\n", recv.reason);
     printespec(&(send.errorspecific));
     disablelink (&disable, commhandle, &qname);
     return;
     }
  /* Interpret the Received Operation  */
  if (recv.operation != 0xB101)
     {
     printf("Recvd opr %x instead of opr B301\n", recv.operation);
     disablelink (&disable, commhandle, &qname);
     return;
     }

(11)

  /****************************************/
  /**  disable the link and end program  **/
  /****************************************/
  disablelink (&disable, commhandle, &qname);
  printf("TARGET application completed OK!\n\n");
}    /* End Main */
/*****************************************************************/
/************** Start Subroutine Section    **********************/
/*****************************************************************/
/*****************************************************************/
/************  Routine to fill X.25 Format I   ***********/
void sndformat1 (sendparms *send,
                 char *buffer,
                 char *rmtdte,
                 char *commhandle,
                 qlindparms *qlind)
{
  format1 *output = (format1 *) buffer;
  register int counter;
  register querydata *qd;
  qd = (querydata *)&(qlind->userbuffer);
  output->type = 0;                 /*  not used  */
  output->logchanid = 0x0;
  output->sendpacksize = qd->x25data.defsend;
  output->sendwindsize = qd->x25data.windowsend;
  output->recvpacksize = qd->x25data.defrecv;
  output->recvwindsize = qd->x25data.windowrecv;
  output->dtelength = strlen(rmtdte);               /*  not used  */
  byte(output->dte, 16, rmtdte, strlen(rmtdte));    /*  not used  */
  output->dbit = 0;
  output->cug = 0;                                  /*  not used  */
  output->cugid = 0;                                /*  not used  */
  output->reverse = 0;                              /*  not used  */
  output->fast = 0;                                 /*  not used  */
  output->faclength = 0;
  byte(output->facilities, 109, "", 0);
  output->calllength = 0;
  byte(output->callud, 128, "00", 2);
  output->misc[0] = 0;
  output->misc[1] = 0;
  output->misc[2] = 0;
  output->misc[3] = 0;
  output->maxasmsize = 16383;
  output->autoflow = 32;
  QOLSEND (&(send->retcode), &(send->reason),
&(send->errorspecific),\
           &(send->newpcep), &(send->ucep), &(send->pcep),\
           commhandle, &(send->operation), &(send->numdtaelmnts));
}  /*  End sndformat1 Subroutine  */
/*****************************************************************/
/************  Routine to fill X.25 Format II   ***********/
void sndformat2 (sendparms *send,
                 char *buffer,
                 char *commhandle)
{
  format2 *output = (format2 *) buffer;
  output->type = 1;
  output->cause = 'FF';
  output->diagnostic = 'FF';
  output->faclength = 0;
  byte(output->facilities, 109, "", 0);
  output->length = 0;
  byte(output->userdata, 128, "", 0);
  QOLSEND (&(send->retcode), &(send->reason),
&(send->errorspecific),\
           &(send->newpcep), &(send->ucep), &(send->pcep),\
           commhandle, &(send->operation), &(send->numdtaelmnts));
}  /*  End sndformat2 Subroutine  */
/********************************************************************/
/**************   Fill in the Buffer for the Filter  ****************/
void setfilters (hdrparms *header)
{
  x25filter *filters;
  filters = (x25filter *)header->filters;
  filters[0].pidlength = 1;
  filters[0].pid = 0x21;            /*  set the protocol ID  */
  filters[0].dtelength = 0;         /* no DTE used in filter */
  byte(filters[0].dte, 12, "", 0);
  filters[0].flags = 0x0;
  filters[0].flags += 0x80;      /* Set Reverse Charging to no  */
  filters[0].flags += 0x40;      /*  Set Fast Select to no  */
}  /*  End setfilters Subroutine  */
/********************************************/
/********    Routine to disable   ***********/
void disablelink (disableparms *disable,
                  char *commhandle,
                  usrspace *qname)
{
  qentry dataq;
  unsigned short expctid;
  disable->vary = 1;     /*  Hard code device to vary off  */
  /**  Call disable link  **/
  QOLDLINK (&(disable->retcode), &(disable->reason),\
                        commhandle, &(disable->vary));
  if ((disable->retcode != 0) && (disable->reason != 00))
     {
     printf ("Link %.10s did not disabled.\n", commhandle);
     printf ("return code = %d\n", disable->retcode);
     printf ("reason code = %d\n\n", disable->reason);
     }
  else
     printf ("%.10s link disabled \n", commhandle);
  /**-------  Set a timer to receive message  --------**/
  expctid = 0xF0F1;
  settimer(&expctid, "Disable  ", &dataq, qname, commhandle);
  if (expctid != 0xF0F1)
     {
     printf("Disable link did not complete successfully");
     return;
     }
  /**  close the files  **/
  fclose(fptr);
  fclose(screen);
}  /*  End disablelink Subroutine  */
/**************************************************************/
/**    Routine to convert string to Hexadecimal format   ******/
void byte (char *dest,
           int dlength,
           char *source,
           int slength)
{
  register int counter;
  char holder[2];
  for (counter=0;counter<dlength;counter++)
   dest[counter]=0;
  for (counter=slength-1;counter>=0;counter--)
   if isxdigit(source[counter])
     {
      holder[0]=source[counter];
      holder[1]='\0';
      if  (counter % 2 == 0)
        dest[counter/2] += (char) hextoint(holder)*16;
      else dest[counter/2] += (char) hextoint(holder);
     }
}  /*  End byte Subroutine  */
/**************************************************************/
/**    Routine to display the ErrorSpecific output       ******/
/**************************************************************/
void printespec(espec *errorspecific)
{
  especout outparms;

  sprintf(outparms.hwecode, "%.8X", errorspecific->hwecode);
  sprintf(outparms.timestamp, "%.8X%.8X", errorspecific->timestamphi,\
                                        errorspecific->timestamplo);
  sprintf(outparms.elogid, "%.8X", errorspecific->elogid);
  if (errorspecific->flags & 0x40)
    outparms.fail = 'Y';
  else outparms.fail = 'N';
  if (errorspecific->flags & 0x20)
    outparms.zerocodes = 'Y';
  else outparms.zerocodes = 'N';
  if (errorspecific->flags & 0x10)
    outparms.qsysopr = 'Y';
  else outparms.qsysopr = 'N';
  sprintf(outparms.cause,"%.2X", errorspecific->cause);
  sprintf(outparms.diagnostic, "%.2X", errorspecific->diagnostic);
  sprintf(outparms.erroffset, "%.6d", errorspecific->erroroffset);
  fwrite(&outparms, 1, sizeof(especout), screen);
  fread("", 0, 0, screen);
}  /*  End printespec Subroutine  */
/***************************************************************/
/*******   Dequeues the Incoming Message and processes it ******/
void dequeue (int length,
              char *key,
              qentry *dataq,
              usrspace *qname)
{
  char fldlen[3],
       waittime[3],
       keylen[2],
       senderid[2],
       *pointer,
       order[2];
  register int counter;
  waittime[0] = 0;
  waittime[1] = 0;
  waittime[2] = 0x1D;   /*  Hard code a delay of infinite   */
  keylen[0] = 0;
  keylen[1] = 0x3F;  /* Hard code a keylength of 3  */
  senderid[0] = 0;
  senderid[1] = 0x0F;
  strncpy(order, "EQ", 2);
  /*  Clear the data structures  **/
  fflush(stdin);
  pointer = (char *)dataq;
  for (counter = 0; counter < 336; counter++)
    pointer[counter] = 0;
  strncpy (dataq->type, "       ", 7);
  while ((strncmp(dataq->type, "*USRDFN", 7) != 0) || (fldlen == 0))
     QRCVDTAQ(qname->name, qname->library, fldlen, dataq, waittime,\
           order, keylen, key, senderid,"");
}  /*  End dequeue Subroutine  */
/**********************************************************/
/*************   Set a timer and dequeue next entry  ******/
void settimer (unsigned short *expctid,
               char *process,
               qentry *dataq,
               usrspace *qname,
               char *commhandle)
{
timerparms timer;
disableparms disable;
int length;
char key[6];
  timer.interval = 20000;              /*  set timer for 20 seconds */
  timer.establishcount = 1;            /*  set establish count to 1 */
  timer.keylength = 3;                 /*  key value  */
  strncpy(timer.keyvalue, "TGT", 3);   /*  set key value /
  timer.operation = 1;                 /*  set a timer  */
  /* Call QOLTIMER   */
  QOLTIMER (&(timer.retcode), &(timer.reason), timer.handleout,\
           timer.handlein, (char *)qname, &(timer.operation),\
           &(timer.interval), &(timer.establishcount),\
           &(timer.keylength), timer.keyvalue, timer.userdata);
  if ((timer.retcode != 0) || (timer.reason != 0))
     {
     printf("%s timer failed while being set.\n", process);
     printf("Return code = %d\n", timer.retcode);
     printf("Reason code = %d\n\n", timer.reason);
     }
  /**-------  Dequeue an entry  --------**/
  strncpy(key, "TGT", 3);
  length = 3;
  dequeue (length, key, dataq, qname);
  /***----     Cancel timer   -----***/
  if (dataq->msgid != 0xF0F4)
     {
     strncpy(timer.handlein, timer.handleout, 8);
     timer.operation = 2;           /*  Cancel one timer  */
     QOLTIMER (&(timer.retcode), &(timer.reason), timer.handleout,\
           timer.handlein, (char *)qname, &(timer.operation),\
           &(timer.interval), &(timer.establishcount),\
           &(timer.keylength), timer.keyvalue, timer.userdata);
     if ((timer.retcode != 0) || (timer.reason != 0))
        {
        printf("%s timer failed while being canceled\n", process);
        printf("Return code = %d\n", timer.retcode);
        printf("Reason code = %d\n\n", timer.reason);
        }
     }
  if (dataq->msgid != *expctid)
     {
     printf ("A %.4X message ID was received instead of %.4X\n",\
              dataq->msgid, *expctid);
     printf ("%s completion message was not received\n", process);
     *expctid = dataq->msgid;
     }
}  /*  End settimer Subroutine  */
/************************************************************/
/**  x25lind:  Read a record into buf and return length   **/
void x25lind (qlindparms *qlind, char *linename)
{
register int counter;
  for(counter=0;counter<256;counter++)
     qlind->userbuffer[counter]=0;
  qlind->format = 0x01;
  QOLQLIND (&(qlind->retcode), &(qlind->reason), &(qlind->nbytes),\
            qlind->userbuffer, linename, &(qlind->format));
}  /*  End x25lind Subroutine  */
/************************************************************/
/**  putdata:  Read a record into buf and return length   **/
void putdata (char *buf,
              int dtalen,
              FILE *fptr)
{
int i;
   for (i = 0; i < dtalen; i++)
      fwrite(buf + i, 1, 1, fptr);
}  /*  End putdata Subroutine  */

Target application program listing references

The following reference numbers and explanations correspond to the reference numbers in the target application's program listing.

(1) Call the C library routines fopen() and signal() to open the target file and set up a signal handler to process IBM i exceptions, respectively. If an exception situation is encountered, the handler() will be called to perform clean-up in order for the program to end.
(2) Call the QOLELINK API to enable the line description using the line name and communications handle passed as input parameters to this program.
(3) Call the QOLTIMER API to time the completion of the enable link operation. If the timer expires before the enable-complete message is posted on the this program's data queue, then this program will end.
(4) Call the QUSPTRUS API to obtain a pointer to the beginning of the output buffer user space. The output buffer will be used to construct a filter list for the call to the QOLSETF API.
(5) Call the QOLRECV API to receive inbound data after reading an incoming data message that was posted on the program's data queue by the user-defined communications support. Since these programs are operating using the communications services of X.25, the first data unit the target program should see is a X'B201' operation signalling an incoming call was received.
(6) Call the QOLSEND API with a X'B400' operation to accept the incoming X.25 call. A connection is now established between the source and target application programs.
(7) The target program will now set a timer by calling the QOLTIMER API and wait for incoming data. If the timer expires before any incoming data is received, then this program will call the QOLDLINK API, and end.
(8) This is the main receive loop for the target program. When data is received from the source program, it will be written to the target file opened during the initialization of this program. The loop will process until a message other than incoming-data entry is read from the program's data queue.
(9) Call the QOLSEND API with a X'B001' operation to locally close the connection.
(10) Receives a X'B101' operation from the user-defined communications support. This is a local confirmation of X'B100' operation.
(11) Call the QOLDLINK API to disable the link previously enabled and end.

Includes for source and target programs

The following three includes are used by both the preceding source and target programs. They are not in an IBM i library.


/*******************************************************************/
/*******************************************************************/
/* Include Name: Header                                            */
/*                                                                 */
/*                                                                 */
/* Function:                                                       */
/*   Type define and declare the structures used to interface      */
/*   to the user-defined communications APIs.  These structures    */
/*   are used by both the source and target application.           */
/*                                                                 */
/*                                                                 */
/* LANGUAGE:  ILE C                                                */
/*                                                                 */
/* APIs USED:  QOLDLINK, QOLELINK, QOLSEND, QOLRECV, QOLSETF,      */
/*             QOLTIMER, QUSPTRUS, QRCVDTAQ, QCLRDTAQ, QOLQLIND    */
/*                                                                 */
/*******************************************************************/
/*******************************************************************/

FILE *screen;
FILE *rptr;
FILE *fptr;

#include <qoldlink.h>
#include <qolelink.h>
#include <qolsend.h>
#include <qolrecv.h>
#include <qolsetf.h>
#include <qoltimer.h>
#include <qusptrus.h>
#include <qrcvdtaq.h>
#include <qclrdtaq.h>
#include <qolqlind.h>

/************   Typedef  Declarations      *******************/

typedef struct usrspace
    {
      char name[10];
      char library[10];
    } usrspace;

typedef struct enableparms     /*  Enable parameters  */
  {
   int retcode,             /*  Output  */
       reason,              /*  Output  */
       tdusize,             /*  Output  */
       numunits,            /*  Output  */
       maxdtalan,           /*  Output  */
       maxdtax25,           /*  Input   */
       keylength;           /*  Input   */
char keyvalue[256],     /*  Input  */
     linename[10];      /*  Input  */
  } enableparms;

typedef struct disableparms     /*  Disable parameters  */
  {
  int retcode,             /*  Output  */
      reason;              /*  Output  */
  char vary;               /*  Input  */
  } disableparms;

typedef struct setfparms     /*  Set Filters parameters  */
  {
  int retcode,             /*  Output  */
      reason,              /*  Output  */
      erroffset;           /*  Output  */
  } setfparms;

typedef _Packed struct hdrparms /* Filter header */
  {
   char function;
   char type;
   unsigned short number;
   unsigned short length;
   char filters[1];
  } hdrparms;

typedef _Packed struct x25filter /* X.25 filter */
  {
   char pidlength;
   char pid;
   char dtelength;
   char dte[12];
   char flags;
  } x25filter;

typedef struct sendparms     /*  Send parameters  */
  {
 espec errorspecific;       /*  Output  */
   int retcode,             /*  Output  */
       reason,              /*  Output  */
       newpcep,             /*  Output  */
       ucep,                /*  Input  */
       pcep,                /*  Input   */
       numdtaelmnts;        /*  Input   */
unsigned short operation;   /*  Input  */
  } sendparms;

typedef struct recvparms     /*  Receive parameters  */
  {
 espec errorspecific;       /*  Output  */
   int retcode,             /*  Output  */
       reason,              /*  Output  */
       newpcep,             /*  Output  */
       ucep,                /*  Output  */
       pcep,                /*  Output   */
       numdtaunits;         /*  Output   */
  char dataavail;           /*  Output  */
unsigned short operation;   /*  Output  */
  } recvparms;

typedef struct timerparms     /*  Timer parameters  */
  {
   int retcode,             /*  Output  */
       reason,              /*  Output  */
       interval,            /*  Input  */
       establishcount,      /*  Input  */
       keylength;           /*  Input   */
  char handleout[8],    /*  Output  */
       handlein[8],     /*  Input  */
       operation,           /*  Input  */
       keyvalue[256],   /*  Input  */
       userdata[60];    /*  Input  */
  } timerparms;


typedef struct especout
  {
   char hwecode[8];
   char timestamp[16];
   char elogid[8];
   char fail;
   char zerocodes;
   char qsysopr;
   char cause[2];
   char diagnostic[2];
   char erroffset[6];
  } especout;

typedef struct qlindparms     /*  Query line parameters  */
  {
   int retcode,             /*  Output  */
       reason,              /*  Output  */
       nbytes;              /*  Output  */
   char userbuffer[256];
   char format;
  } qlindparms;

typedef _Packed union content       /*  Queue support parameters  */
  {
    _Packed struct other
      {
       char commhandle[10];
       char reserved[58];
      } other;
    _Packed struct enable
      {
       char commhandle[10];
       char status;
       char reserved[57];
      } enable;
    _Packed struct timer
      {
       char timerhandle[8];
       char userdata[60];
      } timer;
  } content;

typedef _Packed struct qentry      /*  Queue  parameters  */
  {
    char type[10];
    unsigned short msgid;
    content message;
    char key[256];
  } qentry;

The following typedef include has new type declarations used by both source and target programs.


/*******************************************************************/
/*******************************************************************/
/* Include Name: Typedef                                           */
/*                                                                 */
/* Function:                                                       */
/*   Define the buffer spaces used to pass the data to the         */
/*   APIs.                                                         */
/*                                                                 */
/*                                                                 */
/* LANGUAGE:  ILE C                                                */
/*                                                                 */
/*******************************************************************/
/*******************************************************************/
/*These definitions and C library #include files are either global, or
  are used by multiple modules in the Open FM API driver.*/

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <xxcvt.h>
#include <string.h>
#include <ctype.h>
#include <recio.h>

typedef struct queuein
    {
      char library[10];
      char name[10];
      char option;
    } queuein;

typedef struct namelib
    {
      char library[10];
      char name[10];
    } namelib;

typedef _Packed struct format1
  {
   char type;
   char reserved1;
   unsigned short logchanid;
   unsigned short sendpacksize;
   unsigned short sendwindsize;
   unsigned short recvpacksize;
   unsigned short recvwindsize;
   char reserved2[7];
   char dtelength;
   char dte[16];
   char reserved3[8];
   char dbit;
   char reserved4[7];
   char cug;
   char cugid;
   char reverse;
   char fast;
   char faclength;
   char facilities[109];
   char reserved5[48];
   unsigned short calllength;
   char callud[128];
   char reserved6[128];
   unsigned char misc[4];         /* control flags */
   unsigned int maxasmsize;
   unsigned short autoflow;
} format1;

typedef _Packed struct format2
  {
   unsigned short type;
   char cause;
   char diagnostic;
   char reserved[4];
   char faclength;
   char facilities[109];
   char reserved2[48];
   unsigned short length;
   char userdata[128];
  } format2;

typedef _Packed struct desc
  {
   unsigned short length;
   char more;              /*These 4 char's are only used for X.25.*/
   char qualified;
   char interrupt;
   char dbit;
   char reserved[26];
  } desc;

typedef _Packed struct llcheader
  {
   unsigned short headerlength;
   char macaddr[6];
   char dsap;
   char ssap;
   char priority;
   char priorctl;
   unsigned short routlen;
   unsigned short userdtalen;
   char data[1];
  } llcheader;

typedef _Packed struct espec
  {
   char reserved[2];
   unsigned int hwecode;
   unsigned int timestamphi;
   unsigned int timestamplo;
   unsigned int elogid;
   char reserved2[10];
   char flags;
   char cause;
   char diagnostic;
   char reserved3;
   unsigned int erroroffset;
   char reserved4[4];
  } espec;

typedef struct tableentry
  {
    char handle[10];
    char type;
    char inbuff[20];
    char indesc[20];
    char outbuff[20];
    char outdesc[20];
    unsigned int totaldusize;
    struct tableentry *next;
  } tableentry;

/*******  Data structure for X.25 line      ********/
/*******  descriptions as returned by QOLQLIND.  ******/

typedef struct x25info
  {
   char addrlen;
   char addr[9];
   char addrtype;
   char insert;
   char modulus;
   char dtedce;
   unsigned short maxsend;
   unsigned short maxrecv;
   unsigned short defsend;
   unsigned short defrecv;
   char windowsend;
   char windowrecv;
   unsigned short numlc;
   char lcinfo[4];
  } x25info;

typedef struct querydata
  {
   char header[12];    /* line header info */
   x25info x25data;        /* preliminary data */
  } querydata;

/*******************************************************************/
/*******************************************************************/
/* Include Name: Hexconv                                           */
/*                                                                 */
/* Function:                                                       */
/*   This include brings in procedures to convert hexadecimal      */
/*   to integer values and vice versa.                             */
/*                                                                 */
/*                                                                 */
/* LANGUAGE:  ILE C                                                */
/*                                                                 */
/*******************************************************************/
/*******************************************************************/
#include <stdio.h>

unsigned int hextoint(char *);

char *inttohex(decimal,hex)  /*Converts a 4-byte integer into a
                               string of 2 uppercase hex characters.*/
unsigned int decimal;
char *hex;

{
  sprintf(hex,"%.2X",decimal);
  return(hex);
}

unsigned int hextoint(hex)   /*Converts a string containing hex
                               digits into a 4-byte integer.   */
char *hex;
{
  int decimal;

  sscanf(hex,"%x",&decimal);
  return(decimal);
}