/*
* Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:   
* This file was autogenerated by rpcgen, but should be modified by the developer.
* Make sure you don't use the -component_mod flag in future or this file will be overwritten.
* Fri Oct 10 17:55:34 2003
* Switches Includes
* System Includes
*
*/




#include <stdio.h>
#include <signal.h>
#ifndef WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#else
#include <winsock2.h>
#endif


/****************************************************************************************
 * 
 * Local Includes
 * 
 ***************************************************************************************/
#include "CSvcHacontroller.h"
#include "CSHacontroller.h"
#include "../IntegerAllocatorLibrary/CIntegerAllocator.h"


/****************************************************************************************
 * 
 * Definition
 * 
 ***************************************************************************************/
#define TEMPLATE_HA_CONFIG_FILE			"dynhad.template.conf"
#define MOBILE_AGENT_COMMAND_LINE		"./dynhad --fg --debug --config "
#define MAXCOMMANDLINELENGTH			(31 + 256 + 1)
#define MAXTUNNELS                              32
#define MAXINTERFACECONFIGENTRY			(IFNAMSIZ + 128)


/****************************************************************************************
 * 
 * Macro Functions
 * 
 ***************************************************************************************/
#ifdef WIN32
#define API_SUCCESS                             (0)
#define dynamics_ha_init(a)			(API_SUCCESS)
#define dynamics_ha_get_status(a,b)		(API_SUCCESS)
#define dynamics_ha_get_tunnels(a,b,c)		(API_SUCCESS)
#define dynamics_ha_get_tunnel_info(a,b,c)	(API_SUCCESS)
#define dynamics_ha_destroy_tunnel(a,b)		(API_SUCCESS)
#define dynamics_ha_enable_mobile(a,b,c)	(API_SUCCESS)
#define dynamics_ha_get_care_of_addr(a,b,c)	(API_SUCCESS)
#endif

 
/****************************************************************************************
 * 
 * Implementation
 * 
 ***************************************************************************************/
CSHacontroller::CSHacontroller()
{
	iDynamicsCallTimeout = -1;
	iAliasHostAddress = iAliasInterfaceIndex = 0;
	iAgentProcess = NULL;
}

CSHacontroller::~CSHacontroller()
{
	assert( iAgentProcess == NULL );
}

int CSHacontroller::GetKey()
{
	return iKey;
}

void CSHacontroller::SetKey( int aKey )
{
	iKey = aKey;
}


/****************************************************************************************
 * 
 * PUBLIC FUNCTION: cstr_createagent
 * 
 ***************************************************************************************/
TResult CSHacontroller::cstr_createagent( void )
{
	TResult rv;

	// initialise the result
	rv.iServiceResult = ERR_NONE;
	rv.iSubComponentResult = 0;
	rv.iData0 = 0;
	rv.iData1 = 0;

	// create the virtual network
	rv = CreateVirtualNetwork();
	if( rv.iServiceResult != ERR_NONE ) {
		return rv;
	}

	// create the interface alias
	rv = CreateAliasInterface();
	if( rv.iServiceResult != ERR_NONE ) {
		RemoveVirtualNetwork();
		return rv;
	}

	// create the dynamics config file
	rv = CreateDynamicsConfigFile();
	if( rv.iServiceResult != ERR_NONE ) {
		RemoveVirtualNetwork();
		RemoveAliasInterface();
		return rv;
	}

	// done - success
	return rv;
}


/****************************************************************************************
 * 
 * PUBLIC FUNCTION: dstr_removeagent
 * 
 ***************************************************************************************/
TResult CSHacontroller::dstr_removeagent( int aArgs, int *aDeleteInstance )
{
	TResult rv, rv_config, rv_interface, rv_vn;

	// initialise the result
	rv.iServiceResult = ERR_NONE;
	rv.iSubComponentResult = 0;
	rv.iData0 = 0;
	rv.iData1 = 0;

	// make sure that the proces isn't running
	if( iAgentProcess != NULL ) {
	  *aDeleteInstance = 0;
	  rv.iServiceResult = ERR_INVALIDSTATE;
	  return rv;
	}

	// cleanup the virtual network
	rv_vn = RemoveVirtualNetwork();

	// cleanup the dynamics config file
	rv_config = RemoveDynamicsConfigFile();
	
	// cleanup the alias interface 
	rv_interface = RemoveAliasInterface();

	// check for errors
	if( rv_vn.iServiceResult != ERR_NONE ) {
		return rv_vn;
	}
	if( rv_config.iServiceResult != ERR_NONE ) {
		return rv_config;
	}
	if( rv_interface.iServiceResult != ERR_NONE ) {
		return rv_interface;
	}

	// done - success
	return rv;
}


/****************************************************************************************
 * 
 * PUBLIC FUNCTION: startmobileagent
 * 
 ***************************************************************************************/
TResult CSHacontroller::startmobileagent( int aArgs )
{
	TResult rv;
	int errcode;
	TCAProcessError perr;
	char command_line_str[MAXCOMMANDLINELENGTH];

	// initialise the result
	rv.iServiceResult = ERR_NONE;
	rv.iSubComponentResult = 0;
	rv.iData0 = 0;
	rv.iData1 = 0;

	// check that there is currently no process
	if( iAgentProcess != NULL ) {
		rv.iServiceResult = ERR_INVALIDSTATE;
		rv.iSubComponentResult = 0;
		rv.iData0 = 0;
		return rv;
	}

	// create the new process object
	iAgentProcess = new CAProcess();
	assert( iAgentProcess != NULL );

	// construct the command line
	snprintf( command_line_str, MAXCOMMANDLINELENGTH, "%s%s", MOBILE_AGENT_COMMAND_LINE, iDynamicsConfigFile.GetLocalFilename() );

	// start the actual process
	perr = iAgentProcess->StartProcess( command_line_str, &errcode, false, false, false ); 
	if( perr != CAE_NONE ) {
		rv.iServiceResult = ERR_START_PROCESS_ERROR;
		rv.iSubComponentResult = (int)perr;
		rv.iData0 = errcode;
		delete iAgentProcess;
		iAgentProcess = NULL;
		return rv;
	}

	// done - success
	return rv;
}


/****************************************************************************************
 * 
 * PUBLIC FUNCTION: stopmobileagent
 * 
 ***************************************************************************************/
TResult CSHacontroller::stopmobileagent( int aArgs )
{
	TResult rv;
	TCAProcessError perr;
	TProcessStatus pstatus;

	// initialise the result
	rv.iServiceResult = ERR_NONE;
	rv.iSubComponentResult = 0;
	rv.iData0 = 0;
	rv.iData1 = 0;
	
	// if the process isn't started then invalid state
	if( iAgentProcess == NULL ) {
	  rv.iServiceResult = ERR_INVALIDSTATE;
	  return rv;
	}

	// if the process isn't running then it has died outside the scope of this controller, clean up the state, return an error since
	// this is noteworthy and should be either expected or not happen.
	perr = iAgentProcess->GetProcessStatus( &pstatus );
	assert( perr == CAE_NONE );
	if( pstatus != PS_STARTED ) {
	  assert( (pstatus == PS_STOPPED) || (pstatus == PS_ABANDONNED) );
	  delete iAgentProcess;
	  iAgentProcess = NULL;
	  rv.iServiceResult = ERR_PROCESS_TERMINATED_OUTSIDE_SCOPE;
	  return rv;
	}

	// request the process to stop
	perr = iAgentProcess->RequestStop( SIGTERM );
	if( perr != CAE_NONE ) {
	  rv.iServiceResult = ERR_STOP_PROCESS_ERROR;
	  rv.iSubComponentResult = (int)perr;
	  return rv;
	}

	// wait for the process to stop
	perr = iAgentProcess->WaitForProcessToTerminate( -1 );
	if( perr != CAE_NONE ) {
	  rv.iServiceResult = ERR_WAIT_PROCESS_ERROR;
	  rv.iSubComponentResult = (int)perr;
	  return rv;
	}

	// remove the process
	delete iAgentProcess;
	iAgentProcess = NULL;

	// done - success
	return rv;
}


/****************************************************************************************
 * 
 * PUBLIC FUNCTION: getmobileagentstatus
 * 
 ***************************************************************************************/
TResult CSHacontroller::getmobileagentstatus( int aArgs )
{
	TResult rv;
	TCAProcessError perr;
	TProcessStatus pstatus;

	// initialise the result
	rv.iServiceResult = ERR_NONE;
	rv.iSubComponentResult = 0;
	rv.iData0 = 0;
	rv.iData1 = 0;

	// if the process doesn't exist then we return PS_INVALID
	if( iAgentProcess == NULL ) {
		rv.iData0 = PS_INVALID;
		return rv;
	}

	// otherwise just return whatever iAgentProcess->GetProcessStatus() returns
	perr = iAgentProcess->GetProcessStatus( &pstatus );
	assert( perr == CAE_NONE );
	rv.iData0 = (int)pstatus;
	return rv;
}


/****************************************************************************************
 * 
 * PUBLIC FUNCTION: setsingleoption
 * 
 ***************************************************************************************/
TResult CSHacontroller::setsingleoption( TOptionDesc aArgs )
{
	TResult rv;
	int errcode;
	TDCFError cerr;

	// initialise the result
	rv.iServiceResult = ERR_NONE;
	rv.iSubComponentResult = 0;
	rv.iData0 = 0;
	rv.iData1 = 0;

	// make sure that the process isn't running
	if( iAgentProcess != NULL ) {
		rv.iServiceResult = ERR_INVALIDSTATE;
		return rv;
	}

	// modify the config file
	cerr = iDynamicsConfigFile.SetSingleOption( aArgs.iOptionToken, aArgs.iOptionValue, &errcode );
	if( cerr != DCE_NONE ) {
		rv.iServiceResult = ERR_CONFIG_FILE_ERROR;
		rv.iSubComponentResult = (int)cerr;
		rv.iData0 = errcode;
		return rv;
	}

	// return success
	return rv;
}


/****************************************************************************************
 * 
 * PUBLIC FUNCTION: removesingleoption
 * 
 ***************************************************************************************/
TResult CSHacontroller::removesingleoption( TOptionDesc aArgs )
{
	TResult rv;
	int errcode;
	TDCFError cerr;

	// initialise the result
	rv.iServiceResult = ERR_NONE;
	rv.iSubComponentResult = 0;
	rv.iData0 = 0;
	rv.iData1 = 0;

	// make sure that the process isn't running
	if( iAgentProcess != NULL ) {
		rv.iServiceResult = ERR_INVALIDSTATE;
		return rv;
	}

	// modify the config file
	cerr = iDynamicsConfigFile.RemoveSingleOption( aArgs.iOptionToken, &errcode );
	if( cerr != DCE_NONE ) {
		rv.iServiceResult = ERR_CONFIG_FILE_ERROR;
		rv.iSubComponentResult = (int)cerr;
		rv.iData0 = errcode;
		return rv;
	}

	// return success
	return rv;
}


/****************************************************************************************
 * 
 * PUBLIC FUNCTION: addlistoption
 * 
 ***************************************************************************************/
TResult CSHacontroller::addlistoption( TOptionDesc aArgs )
{
	TResult rv;
	int errcode;
	TDCFError cerr;

	// initialise the result
	rv.iServiceResult = ERR_NONE;
	rv.iSubComponentResult = 0;
	rv.iData0 = 0;
	rv.iData1 = 0;

	// make sure that the process isn't running
	if( iAgentProcess != NULL ) {
		rv.iServiceResult = ERR_INVALIDSTATE;
		return rv;
	}

	// modify the config file
	cerr = iDynamicsConfigFile.AddListOption( aArgs.iOptionBlockStart, aArgs.iOptionBlockEnd, aArgs.iOptionValue, &errcode );
	if( cerr != DCE_NONE ) {
		rv.iServiceResult = ERR_CONFIG_FILE_ERROR;
		rv.iSubComponentResult = (int)cerr;
		rv.iData0 = errcode;
		return rv;
	}

	// return success
	return rv;
}


/****************************************************************************************
 * 
 * PUBLIC FUNCTION: removelistoption
 * 
 ***************************************************************************************/
TResult CSHacontroller::removelistoption( TOptionDesc aArgs )
{
	TResult rv;
	int errcode;
	TDCFError cerr;

	// initialise the result
	rv.iServiceResult = ERR_NONE;
	rv.iSubComponentResult = 0;
	rv.iData0 = 0;
	rv.iData1 = 0;

	// make sure that the process isn't running
	if( iAgentProcess != NULL ) {
		rv.iServiceResult = ERR_INVALIDSTATE;
		rv.iSubComponentResult = 0;
		rv.iData0 = 0;
		return rv;
	}

	// modify the config file
	cerr = iDynamicsConfigFile.RemoveListOption( aArgs.iOptionBlockStart, aArgs.iOptionBlockEnd, aArgs.iOptionToken, &errcode );
	if( cerr != DCE_NONE ) {
		rv.iServiceResult = ERR_CONFIG_FILE_ERROR;
		rv.iSubComponentResult = (int)cerr;
		rv.iData0 = errcode;
		return rv;
	}

	// return success
	return rv;
}


/****************************************************************************************
 * 
 * PUBLIC FUNCTION: getstatus
 * 
 ***************************************************************************************/
THaStatus CSHacontroller::getstatus( int aArgs )
{
	TResult res;
	THaStatus rv;
	TDynamicsCallInfo cres;
	THomeAgentStatusInfo home_agent_info;

	// init the return value
	memset( &rv, 0, sizeof(rv) );
	rv.iCallResult.iServiceResult = ERR_NONE;
	
	// add the info about the virtual network
	rv.iHomeAgentAddress = inet_addr( iAliasInterfaceAddress.c_str() );
	rv.iVirtualNetworkStartAddress = iVirtualNetworkSegmentAddress;
	rv.iVirtualNetworkSize = iVirtualNetworkSegmentSize;

	// if the agent is not running then we are done
	res = is_agent_running();
	if( res.iServiceResult != ERR_NONE ) {
		return rv;
	}

	// make the call to dynamics
	cres = iDynamicsCommand.HomeAgentGetStatus( &home_agent_info );
	if( cres.iResult != DC_SUCCESS ) {
		set_dynamics_error( &(rv.iCallResult), &cres );
		return rv;
	}

	// set the result
	rv.iAdvertisementsSent = home_agent_info.iAdvertisementsSent;
	rv.iDiscardedMalformed = home_agent_info.iDiscardedMalformed;
	rv.iDiscardedUnknownExtension = home_agent_info.iDiscardedUnknownExtension;
	rv.iDiscardedVendor = home_agent_info.iDiscardedVendor;
	rv.iRequestsAccepted = home_agent_info.iRequestsAccepted;
	rv.iRequestsRejected = home_agent_info.iRequestsRejected;
	rv.iTunnelCount = home_agent_info.iTunnelCount;
	return rv;
}


/****************************************************************************************
 * 
 * PUBLIC FUNCTION: destroytunnelid
 * 
 ***************************************************************************************/
TResult CSHacontroller::destroytunnelid( THaTunnelID aArgs )
{
	TResult rv;
	TDynamicsCallInfo cres;

	// init the return value
	memset( &rv, 0, sizeof(rv) );
	rv.iServiceResult = ERR_NONE;
	
	// check that the agent is running
	rv = is_agent_running();
	if( rv.iServiceResult != ERR_NONE ) {
		return rv;
	}

	// make the call to dynamics
	cres = iDynamicsCommand.HomeAgentDestroyTunnel( aArgs.iMobileNodeAddress );
	if( cres.iResult != DC_SUCCESS ) {
		set_dynamics_error( &rv, &cres );
		return rv;
	}

	// done 
	return rv;
}


/****************************************************************************************
 * 
 * PUBLIC FUNCTION: listtunnels
 * 
 ***************************************************************************************/
THaTunnelList CSHacontroller::listtunnels( int aArgs )
{
	TResult res;
	THaTunnelList rv = { 0, NULL };
	TDynamicsCallInfo cres;
	unsigned int tunnel_list[MAXTUNNELS];
	int tunnel_count = MAXTUNNELS, i;

	// check that the agent is running
	res = is_agent_running();
	if( res.iServiceResult != ERR_NONE ) {
		return rv;
	}

	// make the call to dynamics
	cres = iDynamicsCommand.HomeAgentGetTunnels( &tunnel_count, tunnel_list );
	if( cres.iResult != DC_SUCCESS ) {
		return rv;
	}

	// allocate memory for the list
	rv.THaTunnelList_len = tunnel_count;
	rv.THaTunnelList_val = (THaTunnelID*)calloc( tunnel_count, sizeof(THaTunnelID) );
	assert( rv.THaTunnelList_val != NULL );

	// now return the information
	for( i = 0; i < tunnel_count; i++ ) {
		(rv.THaTunnelList_val)[i].iAgentID = 0;
		(rv.THaTunnelList_val)[i].iMobileNodeAddress = tunnel_list[i];
	}

	// done - success
	return rv;
}


/****************************************************************************************
 * 
 * PUBLIC FUNCTION: gettunnelinfo
 * 
 ***************************************************************************************/
THaTunnelInfo CSHacontroller::gettunnelinfo( TGetTunnelRequest aArgs )
{
	THaTunnelInfo rv;
	TDynamicsCallInfo cres;
	THomeAgentTunnelInfo hati;

	// init the result
	memset( &rv, 0, sizeof(rv) );
	rv.iCallResult.iServiceResult = ERR_NONE;

	// check that the agent is running
	rv.iCallResult = is_agent_running();
	if( rv.iCallResult.iServiceResult != ERR_NONE ) {
		return rv;
	}

	// make the call to dynamics
	cres = iDynamicsCommand.HomeAgentGetTunnelInfo( aArgs.iMobileNodeAddress, &hati );
	if( cres.iResult != DC_SUCCESS ) {
		set_dynamics_error( &(rv.iCallResult), &cres );
		return rv;
	}

	// set the info
	rv.iCareofAddress = hati.iCareofAddress;
	rv.iCreationTime = hati.iCreationTime;
	rv.iExpirationTime = hati.iExpirationTime;
	rv.iHomeAgentAddress = hati.iHomeAgentAddress;
	rv.iLastTimestamp = hati.iLastTimestamp;
	rv.iMobileNodeAddress = hati.iMobileNodeAddress;
	rv.iRefreshTime = hati.iRefreshTime;
	rv.iSPI = hati.iSPI;
	rv.iTimeout = hati.iTimeout;
	return rv;
}


/****************************************************************************************
 * 
 * PUBLIC FUNCTION: settimeout
 * 
 ***************************************************************************************/
void CSHacontroller::settimeout( TTimeoutRequest aArgs )
{
	iDynamicsCallTimeout = aArgs.iTimeout;
}


/****************************************************************************************
 * 
 * PRIVATE FUNCTION: CreateDynamicsConfigFile
 * 
 ***************************************************************************************/
TResult CSHacontroller::CreateDynamicsConfigFile()
{
	TResult rv;
	int errcode;
	TDCFError config_file_error;
	char dynamics_config[MAXINTERFACECONFIGENTRY];
	struct in_addr iaddr;
	TStartupInfo startupinfo;

	// initialise the rv - always be optimistic!
	rv.iServiceResult = ERR_NONE;
	rv.iSubComponentResult = 0;
	rv.iData0 = 0;

	// set the reference file
	config_file_error = iDynamicsConfigFile.SetReferenceFile( TEMPLATE_HA_CONFIG_FILE );
	if( config_file_error != DCE_NONE ) {
		rv.iServiceResult = ERR_CONFIG_FILE_ERROR;
		rv.iSubComponentResult = (int)config_file_error;
		return rv;
	}

	// create a local copy
	config_file_error = iDynamicsConfigFile.CreateLocalCopy( &errcode );
	if( config_file_error != DCE_NONE ) {
		rv.iServiceResult = ERR_CONFIG_FILE_ERROR;
		rv.iSubComponentResult = (int)config_file_error;
		rv.iData0 = errcode;
		return rv;
	}

	// get the startup info
	startupinfo = CSvcHacontroller::GetStartupInfo();

	// add the listen interface
	snprintf( dynamics_config, MAXINTERFACECONFIGENTRY, "eth%d:%d 0 1 30", startupinfo.iBaseInterfaceIndex, iAliasInterfaceIndex );
	config_file_error = iDynamicsConfigFile.AddListOption( "INTERFACES_BEGIN", "INTERFACES_END", dynamics_config, &errcode );
	if( config_file_error != DCE_NONE ) {
		rv.iServiceResult = ERR_CONFIG_FILE_ERROR;
		rv.iSubComponentResult = (int)config_file_error;
		rv.iData0 = errcode;
		iDynamicsConfigFile.RemoveLocalCopy( &errcode );
		return rv;
	}

	// add the home network addresses to the authorized list
	iaddr.ADDRESS_INTEGER = iVirtualNetworkSegmentAddress;
	snprintf( dynamics_config, MAXINTERFACECONFIGENTRY, "1000 %s/%d", inet_ntoa(iaddr), iVirtualNetworkSegmentNetmaskBitcount );
	config_file_error = iDynamicsConfigFile.AddListOption( "AUTHORIZEDLIST_BEGIN", "AUTHORIZEDLIST_END", dynamics_config, &errcode );
	if( config_file_error != DCE_NONE ) {
		rv.iServiceResult = ERR_CONFIG_FILE_ERROR;
		rv.iSubComponentResult = (int)config_file_error;
		rv.iData0 = errcode;
		iDynamicsConfigFile.RemoveLocalCopy( &errcode );
		return rv;
	}
	
	// done
	return rv;
}


/****************************************************************************************
 * 
 * PRIVATE FUNCTION: RemoveDynamicsConfigFile
 * 
 ***************************************************************************************/
TResult CSHacontroller::RemoveDynamicsConfigFile()
{
	TResult rv;
	TDCFError terr;
	int errcode;

	// initialise the rv - always be optimistic!
	rv.iServiceResult = ERR_NONE;
	rv.iSubComponentResult = 0;
	rv.iData0 = 0;

	// clean up the config file
	terr = iDynamicsConfigFile.RemoveLocalCopy( &errcode );
	if( terr != DCE_NONE ) {
		rv.iServiceResult = ERR_CONFIG_FILE_ERROR;
		rv.iSubComponentResult = (int)terr;
		rv.iData0 = errcode;
		return rv;
	}

	// done
	return rv;
}


/****************************************************************************************
 * 
 * PRIVATE FUNCTION: CreateAliasInterface
 * 
 ***************************************************************************************/
TResult CSHacontroller::CreateAliasInterface()
{
	TResult rv;
	int errcode;
	CIntegerAllocator *alias_host_address_allocator;
	TStartupInfo startup_info;
	TInterfaceAliasError interface_alias_error;

	// initialise the rv - always be optimistic!
	rv.iServiceResult = ERR_NONE;
	rv.iSubComponentResult = 0;
	rv.iData0 = 0;

	// get the host address allocator
	alias_host_address_allocator = CSvcHacontroller::GetAliasHostAddressAllocator();

	// get a host address
	assert( alias_host_address_allocator != NULL );
	iAliasHostAddress = alias_host_address_allocator->AllocateInteger();
	if( iAliasHostAddress == -1 ) {
		rv.iServiceResult = ERR_NO_MORE_INTERFACES;
		return rv;
	}
//	fprintf( stderr, "DEBUG: iAliasHostAddress = %d\n", iAliasHostAddress );

	// get the startup info
	startup_info = CSvcHacontroller::GetStartupInfo();

	// create an alias
	interface_alias_error = iAgentInterface.CreateNewInterfaceAlias( startup_info.iBaseInterfaceIndex, 
									 startup_info.iBaseInterfaceNetworkMask, 
									 iAliasHostAddress, &iAliasInterfaceIndex, &errcode );
	if( interface_alias_error != IE_NONE ) {
	  rv.iServiceResult = ERR_CREATE_INTERFACE_ERROR;
	  rv.iSubComponentResult = (int)interface_alias_error;
	  rv.iData0 = errcode;
	  alias_host_address_allocator->FreeInteger( iAliasHostAddress );
	  return rv;
	}

	// get the complete interface address as a string
	interface_alias_error = iAgentInterface.GetInterfaceAddress( &iAliasInterfaceAddress );
	assert( interface_alias_error == IE_NONE );

	// done success
	return rv;
}


/****************************************************************************************
 * 
 * PRIVATE FUNCTION: RemoveAliasInterface
 * 
 ***************************************************************************************/
TResult CSHacontroller::RemoveAliasInterface()
{
	int err, errcode;
	TResult rv;
	TInterfaceAliasError interface_alias_error;
	CIntegerAllocator *alias_host_address_allocator;

	// initialise the rv - always be optimistic!
	rv.iServiceResult = ERR_NONE;
	rv.iSubComponentResult = 0;
	rv.iData0 = 0;
	
	// destroy the interface alias
	interface_alias_error = iAgentInterface.DestroyInterfaceAlias( &errcode );
	if( interface_alias_error != IE_NONE ) {
		rv.iServiceResult = ERR_DESTROY_INTERFACE_ERROR;
		rv.iSubComponentResult = (int)interface_alias_error;
		rv.iData0 = errcode;
		return rv;
	}

	// get the host address allocator
	alias_host_address_allocator = CSvcHacontroller::GetAliasHostAddressAllocator();

	// free the host address
	err = alias_host_address_allocator->FreeInteger( iAliasHostAddress );
	assert( err == 0 );

	// done
	iAliasHostAddress = iAliasInterfaceIndex = 0;
	return rv;
}


/****************************************************************************************
 * 
 * PRIVATE FUNCTION: CreateVirtualNetwork
 * 
 ***************************************************************************************/
TResult CSHacontroller::CreateVirtualNetwork()
{
	TResult rv;
	int segment_size, err, segment_netmask_bitcount;
	CNetworkPartitionManager *virtual_network_partition_allocator;

	// init the rv
	memset( &rv, 0, sizeof(rv) );
	rv.iServiceResult = ERR_NONE;

	// get the virtual network partition allocator
	virtual_network_partition_allocator = CSvcHacontroller::GetNetworkPartitionManager();
	assert( virtual_network_partition_allocator != NULL );

	// get a virtual network partition to use
	err = virtual_network_partition_allocator->AllocateNetworkSegment( &segment_netmask_bitcount, &segment_size );
	if( err == -1 ) {
		rv.iServiceResult = ERR_NO_MORE_VIRTUAL_NETWORKS;
		return rv;
	}

	// save this information 
	iVirtualNetworkSegmentAddress = err;
	iVirtualNetworkSegmentSize = segment_size;
	iVirtualNetworkSegmentNetmaskBitcount = segment_netmask_bitcount;

	// done - ok
	rv.iServiceResult = ERR_NONE;
	return rv;
}


/****************************************************************************************
 * 
 * PRIVATE FUNCTION: RemoveVirtualNetwork
 * 
 ***************************************************************************************/
TResult CSHacontroller::RemoveVirtualNetwork()
{
	int err;
	TResult rv;
	CNetworkPartitionManager *virtual_network_partition_allocator;

	// init the rv
	memset( &rv, 0, sizeof(rv) );
	rv.iServiceResult = ERR_NONE;

	// get the virtual network partition allocator
	virtual_network_partition_allocator = CSvcHacontroller::GetNetworkPartitionManager();
	assert( virtual_network_partition_allocator != NULL );

	// free the network address
	err = virtual_network_partition_allocator->FreeNetworkSegment( iVirtualNetworkSegmentAddress );
	assert( err == 0 );

	// clear-up the state
	iVirtualNetworkSegmentAddress = iVirtualNetworkSegmentSize = 0;

	// done 
	rv.iServiceResult = ERR_NONE;
	return rv;
}


/****************************************************************************************
 * 
 * PRIVATE FUNCTION: is_agent_running
 * 
 ***************************************************************************************/
TResult CSHacontroller::is_agent_running()
{	
	TResult rv;
	TCAProcessError perr;
	TProcessStatus pstatus;

	// init the rv
	memset( &rv, 0, sizeof(rv) );
	rv.iServiceResult = ERR_NONE;

	// check that the agent was running
	if( iAgentProcess == NULL ) {
		rv.iServiceResult = ERR_INVALIDSTATE;
		return rv;
	}

	// check that the agent is still runnning
	perr = iAgentProcess->GetProcessStatus( &pstatus );
	assert( perr == CAE_NONE );
	if( pstatus != PS_STARTED ) {
		rv.iServiceResult = ERR_INVALIDSTATE;
		return rv;
	}

	// done - success
	return rv;
}


/****************************************************************************************
 * 
 * PRIVATE FUNCTION: is_agent_running
 * 
 ***************************************************************************************/
void CSHacontroller::set_dynamics_error( TResult *result, TDynamicsCallInfo *cres )
{
	result->iServiceResult = ERR_DYNAMICS_CALL_FAILED;
	result->iSubComponentResult = cres->iResult;
	result->iData0 = cres->iErrorCode;
	result->iData1 = cres->iErrorDetail;
}
