MPINGD.C
/***************************************************************************** 
 * 
 *  MODULE NAME: APINGD.C 
 * 
 *  COPYRIGHTS: 
 *             This module contains code made available by IBM 
 *             Corporation on an AS IS basis.  Any one receiving the 
 *             module is considered to be licensed under IBM copyrights 
 *             to use the IBM-provided source code in any way he or she 
 *             deems fit, including copying it, compiling it, modifying 
 *             it, and redistributing it, with or without 
 *             modifications.  No license under any IBM patents or 
 *             patent applications is to be implied from this copyright 
 *             license. 
 * 
 *             A user of the module should understand that IBM cannot 
 *             provide technical support for the module and will not be 
 *             responsible for any consequences of use of the program. 
 * 
 *             Any notices, including this one, are not to be removed 
 *             from the module without the prior written consent of 
 *             IBM. 
 * 
 *  AUTHOR:    Peter J. Schwaller 
 *             VNET:     PJS at RALVM6           Tie Line: 444-4376 
 *             Internet: pjs@ralvm6.vnet.ibm.com     (919) 254-4376 
 * 
 *  FUNCTION:  Perform an echo test to a specified LU. 
 *             APING can be used when you are first installing APPC on 
 *             your computer to make sure you can connect to another 
 *             computer in the network.  APING can also be used to 
 *             get an estimate of the delay time or throughput to another 
 *             computer in the network. 
 * 
 *             APINGD echos whatever is sent by APING. 
 *                Keep receiving until you get permission to send 
 *                Send the same number of same size records 
 * 
 *  AVAILABILITY: 
 *             These sample programs and source are also available on 
 *             CompuServe through the APPC Information Exchange.  To get 
 *             to the APPC forum just type 'GO APPC' from any CompuServe 
 *             prompt.  The samples are available in the Sample Programs 
 *             library section.  Just search on the keyword CPICPGMS to 
 *             find all the samples in this series. 
 * 
 *             Updates for the sample programs and support for many more 
 *             CPI-C platforms will also be made available on CompuServe. 
 * 
 *  RELATED FILES: 
 *             See APING.DOC for usage instructions. 
 * 
 *  PORTABILIITY NOTES: 
 *             The APINGD server program is completely portable.  In fact, 
 *             all of the source modules can be compiled without #define-ing 
 *             any platform constant value. 
 * 
 *             To take advantage of a performance optimization on the 
 *             OS/2 platform, the alloc_cpic_buffer() is used.  If the 
 *             OS/2 platform is specified (#define of OS2, FAPI, or OS2_20) 
 *             alloc_cpic_buffer() will return a shared memory buffer. 
 *             If not, a buffer allocated with malloc() will be returned. 
 * 
 *             If you are porting to a platform that can take advantage 
 *             of a specially allocated memory buffer, you should 
 *             add this support to the alloc_cpic_buffer() call in the 
 *             CPICPORT.C file. 
 * 
 *  CHANGE HISTORY: 
 *  Date       Description 
 *  06/15/92   NS/DOS accepts version 2.02 into system test. 
 *  08/05/92   Version 2.31 released to CompuServe 
 *             This version was also distributed at the APPC/APPN Platform 
 *             Developer's Conference held in Raleigh, NC. 
 *  08/13/92   Changed all printf and fprintf calls to use a write_*() call. 
 *  08/24/92   Version 2.32 released to CompuServe. 
 *  09/22/92   Version 2.33 released to CompuServe. 
 *  11/17/92   Supports sending operating system string - see CPICERR.C 
 *             Version 2.34 released to CompuServe 
 *  01/07/93   Version 2.35 
 *             Fixed a number of problems when compiling with IBM C Set/2 
 *                password input was displayed 
 *                timer resolution was 1 second 
 *  24/02/94   Close thread handles 
 * 
 *****************************************************************************/ 
 
#ifdef WIN32                                                           /*WIN32*/ 
#include <windows.h>                                                   /*WIN32*/ 
HANDLE hWaitEvent;                                                     /*WIN32*/ 
void __cdecl main( DWORD argc, LPSTR * argv); 
void WINAPI ServiceMain(DWORD dwNumServiceArgs, LPTSTR * lpServiceArgs); 
void TPStart( DWORD argc, LPSTR *argv); 
DWORD WINAPI ThreadStart( int threadarg ); 
VOID WINAPI ControlHandler(DWORD dwControl); 
SERVICE_STATUS_HANDLE stat_hand = {0}; 
SERVICE_STATUS servstat = {0}; 
#endif                                                                 /*WIN32*/ 
 
#include "wincpic.h" 
 
/* standard C include files */ 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
 
/* Set up constant declarations */ 
#include "cpicdefs.h" 
 
/* Collection of routines with special ported version for each platform */ 
#include "cpicport.h" 
 
/* CPI-C error handling routines */ 
/* This file is supplied with APINGD */ 
#include "cpicerrs.h" 
 
/* CPI-C initialization routines */ 
/* This file is supplied with APINGD */ 
#include "cpicinit.h" 
 
/* CPI-C error handling info */ 
CPICERR * cpicerr = {0}; 
 
/* 
 * Max size of a data buffer.  This is the largest size buffer that can 
 * be specified on a call to CPI-C. 
 */ 
#define  MAX_SIZE (0x7FFF) 
 
/* Define these here so we can make changes throughout the code. */ 
/* 
 * The PROGRAM_INFO string should be kept in sync with the 
 * MAJOR_VERSION and MINOR_VERSION constants.  Although the 
 * cpicerr_exchange_version() call will support values up to 255, 
 * values for MINOR_VERSION should be from 00-99 to maintain the 
 * two character format in the version string. 
 */ 
#define  PROGRAM_NAME      "APINGD" 
#define  PROGRAM_INFO      "version 2.35" 
#define  MAJOR_VERSION     (2) 
#define  MINOR_VERSION     (35) 
#define  LOG_FILE_NAME     "apingd.err" 
#define  LOG_FILE_PATH     "$LOGPATH" 
 
void TPStart( DWORD argc, LPSTR *argv) 
{ 
    DWORD Tid; 
    HANDLE      ThreadHandle;               /* thread handle for closing     */ 
    CM_INT32    temp;                                                 /*WIN32*/ 
    CM_INT32    cm_rc;                      /* CPI-C return code             */ 
 
    { 
       /**********************************************************************/ 
       /* Initialisation for WinCPIC                                         */ 
       /**********************************************************************/ 
       unsigned short WinCPICVERSION = 0x0001; 
       WCPICDATA CPICData; 
       if (WinCPICStartup(WinCPICVERSION,&CPICData)) 
       { 
         return; 
       } 
       /**********************************************************************/ 
       /* Set our local TP Name                                              */ 
       /**********************************************************************/ 
       temp=6; 
       cmsltp("APINGD",&temp,&cm_rc); 
    } 
 
    /* 
     * Initialize the CPICERR structure.  This is done before the CMACCP 
     * call so that we can use CPICERR for help with errors on CMACCP. 
     * The procedure is in CPICERR.C 
     */ 
    cpicerr = cpicerr_new(); 
    cpicerr_set_program_name(cpicerr, PROGRAM_NAME); 
    cpicerr_set_program_info(cpicerr, PROGRAM_INFO); 
    cpicerr_set_major_version(cpicerr, MAJOR_VERSION); 
    cpicerr_set_minor_version(cpicerr, MINOR_VERSION); 
    cpicerr_set_log_file_name(cpicerr, LOG_FILE_NAME); 
    cpicerr_set_log_file_path(cpicerr, LOG_FILE_PATH); 
 
    /* 
     * Create the first receive thread. 
     */ 
    ThreadHandle = CreateThread(NULL, 
                                16000, 
                                (LPTHREAD_START_ROUTINE)ThreadStart, 
                                NULL, 
                                0, 
                                &Tid); 
    if (ThreadHandle == NULL) 
    { 
       GetLastError(); 
       DebugBreak(); 
    } 
    CloseHandle(ThreadHandle); 
 
    /* 
     * As this is effectively the ServiceMain routine, we should not return 
     * here until we are ready to die. We wait for an event to signal. 
     */ 
 
    WaitForSingleObject( hWaitEvent, INFINITE ); 
 
    /* 
     * Cleanup WinCPIC now. 
     */ 
 
    WinCPICCleanup(); 
 
} 
 
DWORD WINAPI ThreadStart( int threadarg )                              /*WIN32*/ 
{ 
    /* Variables used for CPI-C calls */ 
    unsigned char cm_conv_id[8];            /* CPI-C conversation ID         */ 
    CM_INT32    temp;                                                 /*WIN32*/ 
    DWORD       Tid;                                                  /*WIN32*/ 
    HANDLE      ThreadHandle;               /* thread handle for closing     */ 
    CM_INT32    cm_rc;                      /* CPI-C return code             */ 
    CM_INT32    length;                     /* generic length variable       */ 
    CM_INT32    rts_received;               /* request to send received      */ 
    CM_INT32    max_receive_len;            /* Max receive length on CMRCV   */ 
    CM_INT32    data_received;              /* Data received parm from CMRCV */ 
    CM_INT32    received_len;               /* Amount of data rcvd on CMRCV  */ 
    CM_INT32    status_received;            /* Status from CMRCV             */ 
 
    /* Data buffer for send and receive */ 
    unsigned char CM_PTR buffer = NULL;    /* CPIC data buffer              */ 
 
    char        destination[MAX_DESTINATION];/* Partner destination          */ 
    unsigned int max_size;                  /* size to receive               */ 
 
    char partner_major_version; 
    char partner_minor_version; 
 
    cmaccp(cm_conv_id,                      /* Accept Conversation           */ 
           &cm_rc); 
    /* 
     * Note that as we have used cmsltp to specify our local TP name, 
     * cmaccp may return asynchronously, so we must do a cmwait 
     */ 
    if (cm_rc == CM_OPERATION_INCOMPLETE)                             /*WIN32*/ 
    {                                                                 /*WIN32*/ 
      cmwait(cm_conv_id, &cm_rc, &temp);                              /*WIN32*/ 
    }                                                                 /*WIN32*/ 
 
    /* 
     * Create the next receive thread. Note if this fails, we will die 
     * horribly by kicking the main thread's wait event. 
     */ 
    ThreadHandle = CreateThread(NULL, 
                                16000, 
                                (LPTHREAD_START_ROUTINE)ThreadStart, 
                                NULL, 
                                0, 
                                &Tid); 
    if (ThreadHandle == NULL) 
    { 
      SetEvent(hWaitEvent); 
    } 
    else 
    { 
      CloseHandle(ThreadHandle); 
    } 
 
    /* 
     * Fill in conversation information for CPI-C error reporting. 
     */ 
    cpicerr_set_conv_id(cpicerr, cm_conv_id); 
 
 
    if (cm_rc != CM_OK) { 
        cpicerr_handle_rc(cpicerr, MSG_CMACCP, cm_rc); 
    } else { 
        CM_INT32 pln_length; 
        /* 
         * Extract the partner LU name and display it. 
         */ 
        cmepln(cm_conv_id, 
               (unsigned char *)destination, 
               &pln_length, 
               &cm_rc ); 
 
        destination[(int)pln_length] = '\0'; 
        write_output("\nContacted by partner: %s\n", destination); 
 
    } 
 
 
    { 
    CM_PREPARE_TO_RECEIVE_TYPE prep_to_receive = CM_PREP_TO_RECEIVE_FLUSH; 
    cmsptr(cm_conv_id,                       /* Set prepare to receive type  */ 
           &prep_to_receive, 
           &cm_rc); 
    if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSPTR, cm_rc); 
    } 
 
 
    max_receive_len = max_size = MAX_SIZE; 
 
    buffer = (unsigned char CM_PTR)alloc_cpic_buffer(max_size); 
                                            /* allocate a buffer             */ 
 
    cpicerr_exchange_version(cpicerr, 
                             cm_conv_id, 
                             CM_RECEIVE_STATE, 
                             &partner_major_version, 
                             &partner_minor_version); 
 
    do { 
        unsigned long count;                /* number of consecutive         */ 
                                            /* sends or receives             */ 
        count = 0;                          /* initialize count of recvs     */ 
        do { 
           cmrcv (cm_conv_id,               /* Receive Data                  */ 
                  buffer,                   /* Data Pointer                  */ 
                  &max_receive_len,         /* Size of Data Buffer           */ 
                  &data_received,           /* returned - data received      */ 
                  &received_len,            /* returned - length of data     */ 
                  &status_received,         /* returned - status received    */ 
                  &rts_received,            /* returned - request to send    */ 
                  &cm_rc); 
 
            if (data_received != CM_NO_DATA_RECEIVED) { 
                count++;                    /* keep track of receives        */ 
            } 
        } while ( (status_received != CM_SEND_RECEIVED) && 
                  (status_received != CM_CONFIRM_RECEIVED) && 
                   !cm_rc); 
        /* 
         * loop until we get permission to send data or until error 
         */ 
 
        if (cm_rc != CM_OK) { 
            if (cm_rc == CM_DEALLOCATED_NORMAL) { 
free_cpic_buffer( buffer ); 
buffer = NULL; 
                do_exit(EXIT_SUCCESS); 
            } else { 
                cpicerr_handle_rc(cpicerr, MSG_CMRCV, cm_rc); 
            } 
        } 
 
        if (status_received != CM_CONFIRM_RECEIVED) { 
            /* 
             * count is now equal to the number of data blocks we received 
             * now we will send back the same number of data blocks of equal 
             * size 
             */ 
            { 
            CM_SEND_TYPE send_type = CM_BUFFER_DATA; 
            cmsst(cm_conv_id, 
                  &send_type, 
                  &cm_rc); 
            if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSST, cm_rc); 
            } 
 
                                 /* send back the same number except for one */ 
            for ( count--; count && !cm_rc; count-- ) { 
                length = received_len; 
                cmsend(cm_conv_id, 
                       buffer, 
                       &length, 
                       &rts_received, 
                       &cm_rc); 
                if (cm_rc != CM_OK) { 
                    cpicerr_handle_rc(cpicerr, MSG_CMSEND, cm_rc); 
                } 
            } 
 
            /* 
             * Set the send type to do a send and a prepare to receive. 
             * This will send both the data and the send permission indicator 
             * to our partner all at once. 
             */ 
            { 
            CM_SEND_TYPE send_type = CM_SEND_AND_PREP_TO_RECEIVE; 
            cmsst(cm_conv_id, 
                  &send_type, 
                  &cm_rc); 
            if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSST, cm_rc); 
            } 
 
            length = received_len; 
            cmsend(cm_conv_id, 
                   buffer, 
                   &length, 
                   &rts_received, 
                   &cm_rc); 
            if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMSEND, cm_rc); 
        } else { 
            /* 
             * The partner has requested one way data transfer only. 
             * We'll just issue Confirmed, then go back up to receive 
             * more data. 
             */ 
            cmcfmd(cm_conv_id, 
                   &cm_rc); 
            if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMCFMD, cm_rc); 
        } 
    } while (cm_rc == CM_OK); 
 
if( buffer != NULL ) 
{ 
free_cpic_buffer( buffer ); 
} 
    /* destroy the object we created with cpicerr_new() */ 
    cpicerr_destroy(cpicerr); 
 
    do_exit(EXIT_SUCCESS); 
 
/* never executed, just to keep compiler happy */ 
return(0); 
} 
 
/*****************************************************************************/ 
/* The following code makes this TP invokable as an NT service. There are 3  */ 
/* routines.                                                                 */ 
/*                                                                           */ 
/* 1. main. This is the entry point for the process, it sets up a service    */ 
/*          table entry and then calls StartServiceCtrlDispatcher. This call */ 
/*          doesn't return, but uses the thread which called it as a         */ 
/*          control dispatcher for all the services implemented by this      */ 
/*          process (in this case, just the TP itself).                      */ 
/*                                                                           */ 
/* 2. ServiceMain. This is the main entry point for the service itself, the  */ 
/*          service control dispatcher creates a thread to start at this     */ 
/*          routine. It must register a service control handler for the      */ 
/*          service which will be called by the control dispatcher when it   */ 
/*          has control instructions for the service. It then informs the    */ 
/*          service control manager that the service is running and finally  */ 
/*          calls the start of the TP itself. This routine should not return */ 
/*          until the service is ready to die.                               */ 
/*                                                                           */ 
/* 3. ControlHandler. This routine is called by the control dispatcher when  */ 
/*          it has instructions for the service. We do not respond to any    */ 
/*          of the instructions as this service should be transitory and not */ 
/*          actually run for more than a few seconds so we don't need to do  */ 
/*          anything with the STOP or SHUTDOWN requests.                     */ 
/*          Note that we MUST call SetServiceStatus, even if the status      */ 
/*          hasn't changed.                                                  */ 
/*****************************************************************************/ 
 
void __cdecl main( DWORD argc, LPSTR * argv) 
{ 
  SERVICE_TABLE_ENTRY  stab[2]; 
 
  /***************************************************************************/ 
  /* Start the control dispatcher. This call gives the SCManager this        */ 
  /* thread for the entire period that this service is running, so that it   */ 
  /* can call us back with service controls. It will spawn a new thread to   */ 
  /* run the service itself, starting at entrypoint ServiceMain.             */ 
  /***************************************************************************/ 
  stab[0].lpServiceName = "APINGD\0"; 
  stab[0].lpServiceProc = ServiceMain; 
 
  stab[1].lpServiceName = NULL; 
  stab[1].lpServiceProc = NULL; 
 
  StartServiceCtrlDispatcher(stab); 
 
} 
 
 
/*****************************************************************************/ 
/* This routine is the entry-point for the service itself the service        */ 
/* control dispatcher creates a thread to start here when we issue           */ 
/* StartServiceControlDispatcher.                                            */ 
/*                                                                           */ 
/* Inputs:  number of arguments to services, array of strings.               */ 
/*                                                                           */ 
/* Outputs: none                                                             */ 
/*                                                                           */ 
/*****************************************************************************/ 
void WINAPI ServiceMain(DWORD dwNumServiceArgs, LPTSTR * lpServiceArgs) 
{ 
 
  DWORD rc; 
 
  stat_hand = RegisterServiceCtrlHandler("APINGD\0", ControlHandler); 
  if (stat_hand == (SERVICE_STATUS_HANDLE)NULL) 
  { 
    rc = GetLastError(); 
    DebugBreak(); 
  } 
 
  if ((hWaitEvent = CreateEvent( NULL, FALSE, FALSE, NULL )) == NULL) 
  { 
    rc = GetLastError(); 
    DebugBreak(); 
  } 
 
  /***************************************************************************/ 
  /* Let the SCManager know that we are running.                             */ 
  /***************************************************************************/ 
  servstat.dwServiceType              = SERVICE_WIN32; 
  servstat.dwCurrentState             = SERVICE_RUNNING; 
  servstat.dwControlsAccepted         = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; 
  servstat.dwWin32ExitCode            = NO_ERROR; 
  servstat.dwServiceSpecificExitCode  = NO_ERROR; 
  servstat.dwCheckPoint               = 0; 
  servstat.dwWaitHint                 = 0; 
 
  rc = SetServiceStatus(stat_hand, &servstat); 
  if (!rc) 
  { 
     rc = GetLastError(); 
     DebugBreak(); 
  } 
 
  TPStart(dwNumServiceArgs, lpServiceArgs); 
 
  servstat.dwCurrentState = SERVICE_STOPPED; 
  rc = SetServiceStatus(stat_hand, &servstat); 
  if (!rc) 
  { 
     rc = GetLastError(); 
     DebugBreak(); 
  } 
 
} 
 
/*****************************************************************************/ 
/* This routine is the callback from the SCManager to handle specific        */ 
/* service control requests. It MUST call SetServiceStatus before it         */ 
/* returns, regardless of whether the status has changed.                    */ 
/*                                                                           */ 
/* Inputs: service control requested                                         */ 
/*                                                                           */ 
/* Outputs: none                                                             */ 
/*                                                                           */ 
/*****************************************************************************/ 
VOID WINAPI ControlHandler(DWORD dwControl) 
{ 
  DWORD rc; 
 
  switch (dwControl) 
  { 
    case SERVICE_CONTROL_PAUSE : 
    case SERVICE_CONTROL_CONTINUE : 
    case SERVICE_CONTROL_INTERROGATE : 
      servstat.dwWaitHint     = 0; 
      break; 
 
 case SERVICE_CONTROL_SHUTDOWN: 
    case SERVICE_CONTROL_STOP : 
      servstat.dwCurrentState = SERVICE_STOP_PENDING; 
      servstat.dwWaitHint     = 10000; 
break; 
  } 
 
  rc=SetServiceStatus(stat_hand, &servstat); 
  if (!rc) 
  { 
     rc=GetLastError(); 
     DebugBreak(); 
  } 
 
  if ((dwControl == SERVICE_CONTROL_STOP) || 
      (dwControl == SERVICE_CONTROL_SHUTDOWN)) 
  { 
     if (!SetEvent(hWaitEvent)) 
     { 
        rc=GetLastError(); 
        DebugBreak(); 
     } 
  } 
}