AREXECD.C

/***************************************************************************** 
*
* MODULE NAME : AREXECD.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: This module is the server side of AREXEC. This module
* will execute a specified command and route the output
* back to the AREXEC transaction program.
*
* 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 AREXEC.DOC for usage instructions.
*
* 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
* arexecd server did not function properly
*
*****************************************************************************/

#ifdef WIN32 /*WIN32*/
#include <windows.h> /*WIN32*/
SERVICE_STATUS_HANDLE stat_hand; /*WIN32*/
SERVICE_STATUS servstat; /*WIN32*/
#endif /*WIN32*/

#include "wincpic.h"

/* standard C include files */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

/* Set up constant declarations */
#include "cpicdefs.h"

/* Collection of routines with special ported version for each platform */
#include "cpicport.h"

#define MAX_COMMAND_LENGTH 500

/*--------------------------------------------------------------------------*/
/* CPI-C Error Handling Global Variables */
/*--------------------------------------------------------------------------*/
#include "cpicerrs.h" /* CPI-C error handling vars. */
CPICERR * cpicerr;

/* 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 "AREXECD"
#define PROGRAM_INFO "version 2.35"
#define MAJOR_VERSION (2)
#define MINOR_VERSION (35)
#define LOG_FILE_NAME "arexecd.err"
#define LOG_FILE_PATH "$LOGPATH"


char * intro[] = {
PROGRAM_NAME " " PROGRAM_INFO " - Remote execution of a command. (Server)",
NULL
};


void
TPStart(void)
{
/* Variables used for CPI-C calls */
unsigned char cm_conv_id[8]; /* CPI-C conversation ID */
CM_INT32 temp; /*WIN32*/
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 what_received; /* What 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 */
char buffer[MAX_COMMAND_LENGTH]; /* CPIC data buffer */

char partner_major_version;
char partner_minor_version;

unsigned char destination[MAX_FQPLU_NAME];

show_info(intro);

#ifdef WIN32
{
/**********************************************************************/
/* Initialisation for WinCPIC */
/**********************************************************************/
unsigned short WinCPICVERSION = 0x0001;
WCPICDATA CPICData;
if (WinCPICStartup(WinCPICVERSION,&CPICData))
{
return;
}
/**********************************************************************/
/* Set our local TP Name */
/**********************************************************************/
temp=7;
cmsltp("AREXECD",&temp,&cm_rc);
}
#endif

/*
* Initialize the CPICERR structure. This is done before the CMINIT
* call so that we can use CPICERR for help with errors on CMINIT.
* 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);

cmaccp(cm_conv_id,
&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*/

/*------------------------------------------------------------------------*
* 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 {
length = 17;
cmepln(cm_conv_id,
destination,
&length,
&cm_rc );
destination[(unsigned int)length] = '\0';
write_output("\nContacted by partner: ");
write_output("%s", destination);
write_output("\n");
}

cpicerr_exchange_version(cpicerr,
cm_conv_id,
CM_RECEIVE_STATE,
&partner_major_version,
&partner_minor_version);

{
CM_SEND_TYPE send_type = CM_SEND_AND_FLUSH;
cmsst(cm_conv_id, /* Set send type */
&send_type,
&cm_rc);
}

max_receive_len = MAX_COMMAND_LENGTH-1;

cmrcv(cm_conv_id, /* Receive Data */
(unsigned char *) buffer, /* Data Pointer */
&max_receive_len, /* Size of Data Buffer */
&what_received, /* returned - what received */
&received_len, /* returned - length of data */
&status_received, /* returned - status received */
&rts_received, /* returned - request to send */
&cm_rc);

if (cm_rc != CM_OK) cpicerr_handle_rc(cpicerr, MSG_CMRCV, cm_rc);

buffer[(unsigned int)received_len] = '\0';

convert_from_ascii(buffer, strlen(buffer));

write_output("The command is:\n%s\n\n", buffer);

execute_and_send_output(buffer,
cm_conv_id,
cpicerr);

{
CM_DEALLOCATE_TYPE deallocate_type = CM_DEALLOCATE_FLUSH;

cmsdt(cm_conv_id,
&deallocate_type,
&cm_rc);
}

cmdeal(cm_conv_id,
&cm_rc);
if (cm_rc) cpicerr_handle_rc(cpicerr, MSG_CMDEAL, cm_rc);

/* destroy the object we created with cpicerr_new() */
cpicerr_destroy(cpicerr);

exit(EXIT_SUCCESS);

}

#ifdef WIN32
/*****************************************************************************/
/* 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);
void WINAPI ServiceMain(DWORD dwNumServiceArgs, LPTSTR * lpServiceArgs);
VOID WINAPI ControlHandler(DWORD dwControl);
SERVICE_STATUS_HANDLE stat_hand;
SERVICE_STATUS servstat;

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 = "AREXECD\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("AREXECD\0", ControlHandler);
if (stat_hand == (SERVICE_STATUS_HANDLE)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();

}

/*****************************************************************************/
/* 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_STOP :
servstat.dwCurrentState = SERVICE_STOP_PENDING;
servstat.dwWaitHint = 24000;
break;

case SERVICE_CONTROL_PAUSE :
case SERVICE_CONTROL_CONTINUE :
case SERVICE_CONTROL_INTERROGATE :
servstat.dwWaitHint = 0;
break;

case SERVICE_CONTROL_SHUTDOWN:
servstat.dwCurrentState = SERVICE_STOP_PENDING;
servstat.dwWaitHint = 10000;
break;
}

rc=SetServiceStatus(stat_hand, &servstat);
if (!rc)
{
rc=GetLastError();
}

}

#endif