libraries/iosrv/client/client_command.cpp
changeset 0 7f656887cf89
child 90 dc41da2f70a4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libraries/iosrv/client/client_command.cpp	Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,445 @@
+// 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
+