testexecmgmt/ucc/Source/facontroller/CSFacontroller.cpp
changeset 0 3da2a79470a7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/testexecmgmt/ucc/Source/facontroller/CSFacontroller.cpp	Mon Mar 08 15:04:18 2010 +0800
@@ -0,0 +1,780 @@
+/*
+* 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.
+* Mon Oct 06 12:02:04 2003
+* System Includes
+*
+*/
+
+
+
+
+
+#include <stdio.h>
+#include <signal.h>
+#ifndef WIN32
+#include <unistd.h>
+#include <net/if.h>
+#endif
+
+
+/****************************************************************************************
+ * 
+ * Local Includes
+ * 
+ ***************************************************************************************/
+#include "CSvcFacontroller.h"
+#include "CSFacontroller.h"
+
+
+/****************************************************************************************
+ * 
+ * Definition
+ * 
+ ***************************************************************************************/
+#define TEMPLATE_CONFIG_FILE			"dynfad.template.conf"
+#define MOBILE_AGENT_COMMAND_LINE		"./dynfad --fg --debug --config "
+#define MAXCOMMANDLINELENGTH			(31 + 256 + 1)
+#define MAXTUNNELS                      32
+#define MAXINTERFACECONFIGENTRY			(IFNAMSIZ + 128)
+#define CONFIGCOUNT						16
+
+
+/*******************************************************************************
+ *
+ * Macro Functions
+ *
+ ******************************************************************************/
+
+
+/****************************************************************************************
+ * 
+ * Implementation
+ * 
+ ***************************************************************************************/
+CSFacontroller::CSFacontroller()
+{
+	iDynamicsCallTimeout = -1;
+	iAgentProcess = NULL;
+}
+
+CSFacontroller::~CSFacontroller()
+{
+	assert( iAgentProcess == NULL );
+}
+
+int CSFacontroller::GetKey()
+{
+	return iKey;
+}
+
+void CSFacontroller::SetKey( int aKey )
+{
+	iKey = aKey;
+}
+
+
+/****************************************************************************************
+ * 
+ * PUBLIC FUNCTION: cstr_createagent
+ * 
+ ***************************************************************************************/
+TResult CSFacontroller::cstr_createagent( void )
+{
+	TResult rv;
+	TDCFError terr, terr_a[CONFIGCOUNT];
+	TInterfaceAliasError ierr;
+	TStartupInfo startup_info;
+	CIntegerAllocator *ialloc;
+	char dynamics_config[MAXINTERFACECONFIGENTRY];
+	string interface_name, interface_address;
+	int errcode, i;
+
+	// initialise the result
+	rv.iServiceResult = ERR_NONE;
+	rv.iSubComponentResult = 0;
+	rv.iData0 = 0;
+	rv.iData1 = 0;
+
+	// get an address for the new interface alias
+	ialloc = CSvcFacontroller::GetIntegerAllocator();
+	assert( ialloc != NULL );
+	iInterfaceHostAddress = ialloc->AllocateInteger();
+	if( iInterfaceHostAddress == -1 ) {
+	  rv.iServiceResult = ERR_NO_MORE_INTERFACES;
+	  return rv;
+	}
+
+	// create a local copy of the configuration file
+	terr = iDynamicsConfigFile.SetReferenceFile( TEMPLATE_CONFIG_FILE );
+	if( terr != DCE_NONE ) {
+	  ialloc->FreeInteger( iInterfaceHostAddress );
+	  rv.iServiceResult = ERR_CONFIG_FILE_ERROR;
+	  rv.iSubComponentResult = (int)terr;
+	  return rv;
+	}
+	terr = iDynamicsConfigFile.CreateLocalCopy( &errcode );
+	if( terr != DCE_NONE ) {
+	  ialloc->FreeInteger( iInterfaceHostAddress );
+	  rv.iServiceResult = ERR_CONFIG_FILE_ERROR;
+	  rv.iSubComponentResult = (int)terr;
+	  rv.iData0 = errcode;
+	  return rv;
+	}	  
+
+	// create an alias interface to use
+	startup_info = CSvcFacontroller::GetStartupInfo();
+	ierr = iAgentInterface.CreateNewInterfaceAlias( startup_info.iBaseInterfaceIndex, startup_info.iNetworkMask, iInterfaceHostAddress, &iInterfaceIndex, &errcode );
+	if( ierr != IE_NONE ) {
+	  rv.iServiceResult = ERR_CREATE_INTERFACE_ERROR;
+	  rv.iSubComponentResult = ierr;
+	  rv.iData0 = errcode;
+	  ialloc->FreeInteger( iInterfaceHostAddress );
+	  iDynamicsConfigFile.RemoveLocalCopy( &errcode );
+	  return rv;
+	}
+	ierr = iAgentInterface.GetInterfaceAddress( &interface_address );
+	assert( ierr == IE_NONE );
+
+	// set the interface name in the config file
+	ierr = iAgentInterface.GetInterfaceName( &interface_name );
+	snprintf( dynamics_config, MAXINTERFACECONFIGENTRY, "%s 2 -1 10", interface_name.c_str() ); 
+	terr_a[0] = iDynamicsConfigFile.AddListOption( "INTERFACES_BEGIN", "INTERFACES_END", dynamics_config, &errcode );
+
+	// set the tunnel name prefix to a unique value for this agent
+	//	snprintf( dynamics_config, MAXINTERFACECONFIGENTRY, "TunnelDevice \"TUNL-%s-\"", interface_name.c_str() );
+	snprintf( dynamics_config, MAXINTERFACECONFIGENTRY, "TunnelDevice \"TUNL\"" );
+	terr_a[1] = iDynamicsConfigFile.SetSingleOption( "TunnelDevice", dynamics_config, &errcode );
+
+	// set the FA addresses to the alias interface address
+	snprintf( dynamics_config, MAXINTERFACECONFIGENTRY, "HighestFAIPAddress %s", interface_address.c_str() );
+	terr_a[2] = iDynamicsConfigFile.SetSingleOption( "HighestFAIPAddress", dynamics_config, &errcode );
+
+	// set the FA addresses to the alias interface address
+	snprintf( dynamics_config, MAXINTERFACECONFIGENTRY, "UpperFAIPAddress %s", interface_address.c_str() );
+	terr_a[3] = iDynamicsConfigFile.SetSingleOption( "UpperFAIPAddress", dynamics_config, &errcode );
+
+	// see if any errors occured setting the config info
+	for( i = 0; i < 3; i++ ) {
+		if( terr_a[i] != DCE_NONE ) {
+			rv.iServiceResult = ERR_CONFIG_FILE_ERROR;
+			rv.iSubComponentResult = (int)terr_a[i];
+			iAgentInterface.DestroyInterfaceAlias( &errcode );
+			ialloc->FreeInteger( iInterfaceHostAddress );
+			iDynamicsConfigFile.RemoveLocalCopy( &errcode );
+			return rv;
+		}
+	}
+
+	// done - success
+	return rv;
+}
+
+
+/****************************************************************************************
+ * 
+ * PUBLIC FUNCTION: dstr_removeagent
+ * 
+ ***************************************************************************************/
+int CSFacontroller::dstr_removeagent( int aArgs, int *aDeleteInstance )
+{
+	int rv = ERR_NONE, errcode;
+	TDCFError terr;
+	TInterfaceAliasError ierr;
+	CIntegerAllocator *ialloc;
+
+	// make sure that the proces isn't running
+	if( iAgentProcess != NULL ) {
+	  *aDeleteInstance = 0;
+	  rv = ERR_INVALIDSTATE;
+	  return rv;
+	}
+
+	// free the interface index
+	ialloc = CSvcFacontroller::GetIntegerAllocator();
+	assert( ialloc != NULL );
+	ialloc->FreeInteger( iInterfaceHostAddress );
+
+	// clean up the config file
+	terr = iDynamicsConfigFile.RemoveLocalCopy( &errcode );
+
+	// destroy the interface alias
+	ierr = iAgentInterface.DestroyInterfaceAlias( &errcode );
+	  
+	// check for errors from the config file - delete the instance in any case
+	if( terr != DCE_NONE ) {
+	  rv = ERR_CONFIG_FILE_ERROR;
+	  return rv;
+	}
+
+	// check for errors from the interface alias - delete the instance
+	if( ierr != IE_NONE ) {
+	  rv = ERR_DESTROY_INTERFACE_ERROR;
+	  return rv;
+	}
+
+	// done - success
+	return rv;
+}
+
+
+/****************************************************************************************
+ * 
+ * PUBLIC FUNCTION: startmobileagent
+ * 
+ ***************************************************************************************/
+TResult CSFacontroller::startmobileagent( TStartAgentRequest aArgs )
+{
+	TResult rv;
+	int errcode;
+	TCAProcessError perr;
+	char command_line_str[MAXCOMMANDLINELENGTH];
+	TDCFError terr;
+
+	// 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;
+		return rv;
+	}
+
+	// do some semantic checking of the params
+	if( (aArgs.iSolicitationMode < -1) || (aArgs.iSolicitationMode > 1) ) {
+		rv.iServiceResult = ERR_INVALIDARG;
+		rv.iData0 = 3;
+		return rv;
+	}
+	if( (aArgs.iSolicitationMode == 1) && (aArgs.iSolicitationInterval < 1) ) {
+		rv.iServiceResult = ERR_INVALIDARG;
+		rv.iData0 = 4;
+		return rv;
+	}
+	if( aArgs.iSolicitationMode != 1 ) {
+	  aArgs.iSolicitationInterval = 1;
+	}
+
+	// add the interface name to the config file
+	snprintf( command_line_str, MAXCOMMANDLINELENGTH, "%s 3 %d %d", aArgs.iLowerInterface, aArgs.iSolicitationMode, aArgs.iSolicitationInterval );
+	terr = iDynamicsConfigFile.AddListOption( "INTERFACES_BEGIN", "INTERFACES_END", command_line_str, &errcode );
+	if( terr != DCE_NONE ) {
+		rv.iServiceResult = ERR_CONFIG_FILE_ERROR;
+		rv.iSubComponentResult = (int)terr;
+		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;
+		delete iAgentProcess;
+		iAgentProcess = NULL;
+		return rv;
+	}
+	iLowerInterface = aArgs.iLowerInterface;
+
+	// done - success
+	rv.iServiceResult = ERR_NONE;
+	return rv;
+}
+
+
+/****************************************************************************************
+ * 
+ * PUBLIC FUNCTION: stopmobileagent
+ * 
+ ***************************************************************************************/
+TResult CSFacontroller::stopmobileagent( int aArgs )
+{
+	TResult rv;
+	TCAProcessError perr;
+	TProcessStatus pstatus;
+	TDCFError terr;
+	int errcode;
+
+	// 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;
+	}
+
+	// remove the interface line added in startup
+	terr = iDynamicsConfigFile.RemoveListOption( "INTERFACES_BEGIN", "INTERFACES_END", iLowerInterface.c_str(), &errcode );
+	if( terr != DCE_NONE ) {
+		rv.iServiceResult = ERR_CONFIG_FILE_ERROR;
+		rv.iSubComponentResult = (int)terr;
+		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 CSFacontroller::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 CSFacontroller::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
+	rv.iServiceResult = ERR_NONE;
+	return rv;
+}
+
+
+/****************************************************************************************
+ * 
+ * PUBLIC FUNCTION: removesingleoption
+ * 
+ ***************************************************************************************/
+TResult CSFacontroller::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
+	rv.iServiceResult = ERR_NONE;
+	return rv;
+}
+
+
+/****************************************************************************************
+ * 
+ * PUBLIC FUNCTION: addlistoption
+ * 
+ ***************************************************************************************/
+TResult CSFacontroller::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
+	rv.iServiceResult = ERR_NONE;
+	return rv;
+}
+
+
+/****************************************************************************************
+ * 
+ * PUBLIC FUNCTION: removelistoption
+ * 
+ ***************************************************************************************/
+TResult CSFacontroller::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;
+		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
+	rv.iServiceResult = ERR_NONE;
+	return rv;
+}
+
+
+/****************************************************************************************
+ * 
+ * PUBLIC FUNCTION: getstatus
+ * 
+ ***************************************************************************************/
+TFaStatusInfo CSFacontroller::getstatus( int aArgs )
+{
+	TFaStatusInfo rv;
+	TDynamicsCallInfo cres;
+	TForeignAgentStatusInfo foreign_agent_info;
+
+	// init the return value
+	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.ForeignAgentGetStatus( &foreign_agent_info );
+	if( cres.iResult != DC_SUCCESS ) {
+		set_dynamics_error( &(rv.iCallResult), &cres );
+		return rv;
+	}
+
+	// set the result
+	rv.iAdvertisementsSent = foreign_agent_info.iAdvertisementsSent;
+	rv.iDiscardedMalformed = foreign_agent_info.iDiscardedMalformed;
+	rv.iDiscardedUnknownExtension = foreign_agent_info.iDiscardedUnknownExtension;
+	rv.iDiscardedVendor = foreign_agent_info.iDiscardedVendor;
+	rv.iPendingRegistrationRequests = foreign_agent_info.iPendingRegistrationRequests;
+	rv.iReplysAccepted = foreign_agent_info.iReplysAccepted;
+	rv.iReplysRejected = foreign_agent_info.iReplysRejected;
+	rv.iRequestsAccepted = foreign_agent_info.iRequestsAccepted;
+	rv.iRequestsRejected = foreign_agent_info.iRequestsRejected;
+	rv.iTunnelCount = foreign_agent_info.iTunnelCount;
+	return rv;
+}
+
+
+/****************************************************************************************
+ * 
+ * PUBLIC FUNCTION: destroytunnelid
+ * 
+ ***************************************************************************************/
+TResult CSFacontroller::destroytunnelid( TFaTunnelID 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.ForeignAgentDestroyTunnel( aArgs.iMobileNodeAddress );
+	if( cres.iResult != DC_SUCCESS ) {
+		set_dynamics_error( &rv, &cres );
+		return rv;
+	}
+
+	// done 
+	return rv;
+}
+
+
+/****************************************************************************************
+ * 
+ * PUBLIC FUNCTION: listtunnels
+ * 
+ ***************************************************************************************/
+TFaTunnelList CSFacontroller::listtunnels( int aArgs )
+{
+	TResult res;
+	TFaTunnelList rv = { 0, NULL };
+	TDynamicsCallInfo cres;
+	TTunnelID 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.ForeignAgentGetTunnels( &tunnel_count, tunnel_list );
+	if( cres.iResult != DC_SUCCESS ) {
+		return rv;
+	}
+
+	// allocate memory for the list
+	rv.TFaTunnelList_len = tunnel_count;
+	rv.TFaTunnelList_val = (TFaTunnelID*)calloc( tunnel_count, sizeof(TFaTunnelID) );
+	assert( rv.TFaTunnelList_val != NULL );
+
+	// now return the information
+	for( i = 0; i < tunnel_count; i++ ) {
+		(rv.TFaTunnelList_val)[i].iAgentID = 0;
+		(rv.TFaTunnelList_val)[i].iID = (tunnel_list[i]).iTunnelID;
+		(rv.TFaTunnelList_val)[i].iHomeAgentAddress = (tunnel_list[i]).iHomeAgentAddress;
+		(rv.TFaTunnelList_val)[i].iMobileNodeAddress = (tunnel_list[i]).iMobileNodeAddress;
+	}
+
+	// done - success
+	return rv;
+}
+
+/****************************************************************************************
+ * 
+ * PUBLIC FUNCTION: gettunnelinfo
+ * 
+ ***************************************************************************************/
+TFaTunnelInfo CSFacontroller::gettunnelinfo( TGetTunnelRequest aArgs )
+{
+	TFaTunnelInfo rv;
+	TDynamicsCallInfo cres;
+	TForeignAgentTunnelInfo fati;
+
+	// 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.ForeignAgentGetTunnelInfo( aArgs.iMobileNodeAddress, &fati );
+	if( cres.iResult != DC_SUCCESS ) {
+		set_dynamics_error( &(rv.iCallResult), &cres );
+		return rv;
+	}
+
+	// set the info
+	rv.iCareofAddress = fati.iCareofAddress;
+	rv.iCreationTime = fati.iCreationTime;
+	rv.iExpirationTime = fati.iExpirationTime;
+	rv.iHomeAgentAddress = fati.iHomeAgentAddress;
+	rv.iLastTimestamp = fati.iLastTimestamp;
+	rv.iMobileNodeAddress = fati.iMobileNodeAddress;
+	rv.iPrivateHomeAgentID = fati.iPrivateHomeAgentID;
+	rv.iRefreshTime = fati.iRefreshTime;
+	rv.iSPI = fati.iSPI;
+	rv.iTimeout = fati.iTimeout;
+	return rv;
+}
+
+
+
+/****************************************************************************************
+ * 
+ * PUBLIC FUNCTION: settimeout
+ * 
+ ***************************************************************************************/
+void CSFacontroller::settimeout( TTimeoutRequest aArgs )
+{
+	iDynamicsCallTimeout = aArgs.iTimeout;
+	iDynamicsCommand.SetTimeout( aArgs.iTimeout );
+}
+
+
+/****************************************************************************************
+ * 
+ * PRIVATE FUNCTION: is_agent_running
+ * 
+ ***************************************************************************************/
+TResult CSFacontroller::is_agent_running()
+{	
+	TResult rv;
+	TCAProcessError perr;
+	TProcessStatus pstatus;
+
+	// clear the rv
+	memset( &rv, 0, sizeof(rv) );
+
+	// 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 CSFacontroller::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;
+}
+