libraries/iosrv/client/client_command.cpp
author Tom Sutcliffe <thomas.sutcliffe@accenture.com>
Sat, 31 Jul 2010 19:07:57 +0100
changeset 23 092bcc217d9d
parent 0 7f656887cf89
child 90 dc41da2f70a4
permissions -rw-r--r--
Tidied iocli exports, build macro tweaks. Removed 4 overloads of CCommandBase::RunCommand[L] that are no longer used at all, and changed one more to not be exported as it's only used internally to iocli.dll. fixed builds on platforms that don't support btrace or any form of tracing.

// client_command.cpp
// 
// Copyright (c) 2009 - 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "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:
// Accenture - Initial contribution
//

#include <fshell/ioutils.h>
#include <fshell/ltkutils.h>
#include "command_base.h"

namespace IoUtils
	{

_LIT(KNewLine, "\r\n");
_LIT(KOutgoingMarker, ">");
_LIT(KIncomingMarker, "<");


//
// CServerWatcher.
//

NONSHARABLE_CLASS(CServerWatcher) : public CActive
	{
public:
	static CServerWatcher* NewL(const TDesC& aServerExeName, CClientBase& aClient);
	~CServerWatcher();
	void Kill(TInt aReason);
private:
	CServerWatcher(CClientBase& aClient);
	void ConstructL(const TDesC& aServerExeName);
private: // From CActive.
	virtual void RunL();
	virtual void DoCancel();
private:
	CClientBase& iClient;
	RProcess iServerProcess;
	};

CServerWatcher* CServerWatcher::NewL(const TDesC& aServerExeName, CClientBase& aClient)
	{
	CServerWatcher* self = new(ELeave) CServerWatcher(aClient);
	CleanupStack::PushL(self);
	self->ConstructL(aServerExeName);
	CleanupStack::Pop(self);
	return self;
	}

CServerWatcher::~CServerWatcher()
	{
	Cancel();
	iServerProcess.Close();
	}

void CServerWatcher::Kill(TInt aReason)
	{
	Cancel();
	iServerProcess.Kill(aReason);
	}

CServerWatcher::CServerWatcher(CClientBase& aClient)
	: CActive(CActive::EPriorityStandard), iClient(aClient)
	{
	CActiveScheduler::Add(this);
	}

void CServerWatcher::ConstructL(const TDesC& aServerExeName)
	{
	_LIT(KWildCard, "*");
	TName processName(aServerExeName);
	processName.Append(KWildCard);
	TFindProcess findProcess(processName);
	TFullName name;
	StaticLeaveIfErr(findProcess.Next(name), _L("Unable to find server process '%S'"), &aServerExeName);
	StaticLeaveIfErr(iServerProcess.Open(findProcess), _L("Unable to open server process '%S'"), &aServerExeName);
	if (findProcess.Next(name) != KErrNotFound)
		{
		StaticLeaveIfErr(KErrArgument, _L("Found more than one instance of '%S'"), &aServerExeName);
		}
	iServerProcess.Logon(iStatus);
	if (iStatus != KRequestPending)
		{
		User::WaitForRequest(iStatus);
		StaticLeaveIfErr(KErrGeneral, _L("Failed to logon to '%S' - %d"), &aServerExeName, iStatus.Int());
		}
	SetActive();
	}

void CServerWatcher::RunL()
	{
	TExitCategoryName exitCategory(iServerProcess.ExitCategory());
	iClient.HandleServerDeath(iServerProcess.ExitType(), iServerProcess.ExitReason(), exitCategory);
	}

void CServerWatcher::DoCancel()
	{
	iServerProcess.LogonCancel(iStatus);
	}


//
// CServerReader.
//

NONSHARABLE_CLASS(CServerReader) : public CActive
	{
public:
	static CServerReader* NewL(RIoReadHandle& aReadHandle, CClientBase& aClient);
	~CServerReader();
private:
	CServerReader(RIoReadHandle& aReadHandle, CClientBase& aClient);
	void ConstructL();
	void Queue();
private: // From CActive.
	virtual void RunL();
	virtual void DoCancel();
private:
	RIoReadHandle& iReadHandle;
	CClientBase& iClient;
	TBuf<0x200> iLine;
	};

CServerReader* CServerReader::NewL(RIoReadHandle& aReadHandle, CClientBase& aClient)
	{
	CServerReader* self = new(ELeave) CServerReader(aReadHandle, aClient);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

CServerReader::~CServerReader()
	{
	Cancel();
	}

CServerReader::CServerReader(RIoReadHandle& aReadHandle, CClientBase& aClient)
	: CActive(CActive::EPriorityStandard), iReadHandle(aReadHandle), iClient(aClient)
	{
	CActiveScheduler::Add(this);
	}

void CServerReader::Queue()
	{
	iLine.Zero();
	iReadHandle.Read(iLine, iStatus);
	SetActive();
	}

void CServerReader::ConstructL()
	{
	iReadHandle.SetReadModeL(RIoReadHandle::ELine);
	iReadHandle.SetLineSeparatorL(KNewLine());
	Queue();
	}

void CServerReader::RunL()
	{
	TInt err = iStatus.Int();
	if (err == KErrEof)
		{
		iClient.HandleServerReadComplete(KErrServerTerminated, iLine);
		}
	else
		{
		iClient.HandleServerReadComplete(err, iLine);
		}
	Queue();
	}

void CServerReader::DoCancel()
	{
	iReadHandle.ReadCancel();
	}


//
// CServerWriter.
//

NONSHARABLE_CLASS(CServerWriter) : public CActive
	{
public:
	static CServerWriter* NewL(RIoWriteHandle& aWriteHandle, CClientBase& aClient);
	~CServerWriter();
	void Write(const TDesC& aLine);
private:
	CServerWriter(RIoWriteHandle& aWriteHandle, CClientBase& aClient);
private: // From CActive.
	virtual void RunL();
	virtual void DoCancel();
private:
	RIoWriteHandle& iWriteHandle;
	CClientBase& iClient;
	HBufC* iBuf;
	};

CServerWriter* CServerWriter::NewL(RIoWriteHandle& aWriteHandle, CClientBase& aClient)
	{
	return new(ELeave) CServerWriter(aWriteHandle, aClient);
	}

CServerWriter::~CServerWriter()
	{
	Cancel();
	delete iBuf;
	}

CServerWriter::CServerWriter(RIoWriteHandle& aWriteHandle, CClientBase& aClient)
	: CActive(CActive::EPriorityStandard), iWriteHandle(aWriteHandle), iClient(aClient)
	{
	CActiveScheduler::Add(this);
	}

void CServerWriter::Write(const TDesC& aLine)
	{
	ASSERT(!IsActive());
	TInt bufferRequired = aLine.Length();
	TBool newLineRequired(EFalse);
	if ((aLine.Length() < KNewLine().Length()) || (aLine.Right(KNewLine().Length()) != KNewLine))
		{
		newLineRequired = ETrue;
		bufferRequired += KNewLine().Length();
		}
	if (iBuf && (iBuf->Des().MaxLength() < bufferRequired))
		{
		iBuf = iBuf->ReAllocL(bufferRequired);
		}
	else
		{
		iBuf = HBufC::NewL(bufferRequired);
		}
	TPtr ptr(iBuf->Des());
	ptr.Copy(aLine);
	if (newLineRequired)
		{
		ptr.Append(KNewLine);
		}
	iWriteHandle.Write(*iBuf, iStatus);
	SetActive();
	}

void CServerWriter::RunL()
	{
	iClient.HandleServerWriteComplete(iStatus.Int());
	}

void CServerWriter::DoCancel()
	{
	iWriteHandle.WriteCancel();
	}


EXPORT_C CClientBase::CClientBase(TUint aFlags, const TDesC& aServerExeName, const TDesC& aPersistentConsoleName, const TDesC& aServerPrompt)
	: CCommandBase(EManualComplete | aFlags), iServerExeName(aServerExeName), iPersistentConsoleName(aPersistentConsoleName), iServerPrompt(aServerPrompt)
	{
	}

EXPORT_C CClientBase::~CClientBase()
	{
	delete iCommand;
	delete iServerReader;
	delete iServerWriter;
	delete iServerWatcher;
	iServerWritePipe.Close();
	iServerWriteHandle.Close();
	iServerReadPipe.Close();
	iServerReadHandle.Close();
	iPcons.Close();
	iServerProcess.Close();
	iLines.ResetAndDestroy();
	}
	
_LIT(KWritePipeNameFmt, "%S client->server pipe");
_LIT(KWriterNameFmt, "%S server writer");
_LIT(KReadPipeNameFmt, "%S server->client pipe");
_LIT(KReaderNameFmt, "%S server reader");

EXPORT_C void CClientBase::DoRunL()
	{
	TInt err = iPcons.OpenByName(IoSession(), iPersistentConsoleName);
	if (err==KErrNotFound)
		{
		if (iVerbose)
			{
			Printf(_L("'%S' not found, creating...\r\n"), &iPersistentConsoleName);
			}
		LeaveIfErr(iPcons.Create(IoSession(), iPersistentConsoleName, iPersistentConsoleName), _L("Couldn't create persistent console '%S'"), &iPersistentConsoleName);
		TRAPL(iServerProcess.CreateL(iServerExeName, KNullDesC(), IoSession(), iPcons, &Env()), _L("Couldn't create server process"));
		}
	else
		{
		LeaveIfErr(err, _L("Cannot open persistent console '%S'"), &iPersistentConsoleName);
		}

	iServerWatcher = CServerWatcher::NewL(iServerExeName, *this);
		
	TName objName;		

	iServerWritePipe.CreateL(IoSession());
	objName.AppendFormat(KWritePipeNameFmt, &Name());
	IoSession().SetObjectNameL(iServerWritePipe.SubSessionHandle(), objName);

	iServerWriteHandle.CreateL(IoSession());
	objName.Zero();
	objName.AppendFormat(KWriterNameFmt, &Name());
	IoSession().SetObjectNameL(iServerWriteHandle.SubSessionHandle(), objName);
	iServerWriteHandle.SetModeL(RIoWriteHandle::EText);
	iServerWritePipe.AttachL(iServerWriteHandle);
	// attach the pcons reader to the other end of our server write pipe
	LeaveIfErr(iPcons.AttachReader(iServerWritePipe, RIoPersistentConsole::EDetachOnHandleClose), _L("Cannot connect reader to persistent console %S"), &iPersistentConsoleName);
	iServerWriter = CServerWriter::NewL(iServerWriteHandle, *this);

	iServerReadPipe.CreateL(IoSession());
	objName.Zero();
	objName.AppendFormat(KReadPipeNameFmt, &Name());
	IoSession().SetObjectNameL(iServerReadPipe.SubSessionHandle(), objName);
	
	iServerReadHandle.CreateL(IoSession());
	objName.Zero();
	objName.AppendFormat(KReaderNameFmt, &Name());
	IoSession().SetObjectNameL(iServerReadHandle.SubSessionHandle(), objName);
	iServerReadPipe.AttachL(iServerReadHandle, RIoEndPoint::EForeground);
	// attach the pcons writer to the other end of our server read pipe
	LeaveIfErr(iPcons.AttachWriter(iServerReadPipe, RIoPersistentConsole::EDetachOnHandleClose), _L("Cannot connect writer to persistent console %S"), &iPersistentConsoleName);
	iServerReader = CServerReader::NewL(iServerReadHandle, *this);
	
	if (iServerProcess.Process().Handle() != KNullHandle && iServerProcess.Process().Handle() != RProcess().Handle())
		{
		// We created a new server process, but it's not yet been resumed.
		iServerProcess.Detach(); // Note, iServerWatch has already logged onto the process so there's no need to use RChildProcess::Run.
		iWaitingForServerPrompt = ETrue;
		}
	else
		{
		// The server was already running - no need to wait for a prompt, go ahead and write the command.
		SendCommand();
		}
	}

EXPORT_C void CClientBase::ArgumentsL(RCommandArgumentList& aArguments)
	{
	_LIT(KCommand, "command");

	if (UsingCif()) // Test this dynamically for the time being because not all sub-classes have been migrated to CIFs.
		{
		aArguments.AppendStringL(iCommand, KCommand);
		}
	else
		{
		_LIT(KCommandDescription, "The command to run.");
		aArguments.AppendStringL(iCommand, KCommand, KCommandDescription, KValueTypeFlagLast);
		}
	}

EXPORT_C void CClientBase::HandleLeave(TInt aError)
	{
	CCommandBase::HandleLeave(aError);
	}

void CClientBase::HandleServerReadComplete(TInt aError, TDes& aLine)
	{
	if (aError == KErrEof)
		{
		Complete(KErrNone);
		}
	else if (aError)
		{
		iServerWatcher->Kill(KErrAbort);
		Complete(aError, _L("Failed to read server response"));
		}
	else if (aLine == iServerPrompt)
		{
		if (iWaitingForServerPrompt)
			{
			iWaitingForServerPrompt = EFalse;
			SendCommand();
			}
		else
			{
			TRAPD(err, HandleServerResponseL(iLines));
			Complete(err);
			}
		}
	else
		{
		if (iVerbose)
			{
			Write(KIncomingMarker);
			Write(aLine);
			}
		if (!iWaitingForServerPrompt)
			{
			aLine.TrimRight();
			HBufC* buf = aLine.AllocLC();
			iLines.AppendL(buf);
			CleanupStack::Pop(buf);
			}
		}
	}

void CClientBase::HandleServerWriteComplete(TInt aError)
	{
	if (aError)
		{
		iServerWatcher->Kill(KErrAbort);
		Complete(aError, _L("Failed to write command to server"));
		}
	}

void CClientBase::HandleServerDeath(TExitType aExitType, TInt aExitReason, const TDesC& aExitCategory)
	{
	if (aExitType == EExitPanic)
		{
		Complete(KErrServerTerminated, _L("Server '%S' PANIC - %S %d"), &iServerExeName, &aExitCategory, aExitReason);
		}
	else if (aExitReason)
		{
		Complete(aExitReason, _L("Server '%S' exitied abnormally"), &iServerExeName);
		}
	else
		{
		Complete();
		}
	}

void CClientBase::SendCommand()
	{
	if (iVerbose)
		{
		Write(KOutgoingMarker);
		Write(*iCommand);
		Write(KNewLine);
		}
	iServerWriter->Write(*iCommand);
	}

	} // namespace IoUtils