core/src/command_wrappers.cpp
changeset 0 7f656887cf89
child 59 c9dfb364c2d1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/command_wrappers.cpp	Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,789 @@
+// command_wrappers.cpp
+// 
+// Copyright (c) 2006 - 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 "command_wrappers.h"
+
+
+//
+// Constants.
+//
+
+const TInt KMaxHeapSize = KMinHeapSize * 1024;
+
+
+//
+// CCommandWrapperBase.
+//
+
+CCommandWrapperBase::CCommandWrapperBase()
+	{
+	}
+
+CCommandWrapperBase::~CCommandWrapperBase()
+	{
+	iStdin.Close();
+	iStdout.Close();
+	iStderr.Close();
+	delete iName;
+	}
+
+void CCommandWrapperBase::BaseConstructL(const TDesC& aName)
+	{
+	iName = aName.AllocL();
+	}
+
+RIoReadHandle& CCommandWrapperBase::CmndStdin()
+	{
+	return iStdin;
+	}
+
+RIoWriteHandle& CCommandWrapperBase::CmndStdout()
+	{
+	return iStdout;
+	}
+
+RIoWriteHandle& CCommandWrapperBase::CmndStderr()
+	{
+	return iStderr;
+	}
+
+const TDesC& CCommandWrapperBase::CmndName() const
+	{
+	return *iName;
+	}
+
+TInt CCommandWrapperBase::CmndReattachStdin(RIoEndPoint& aStdinEndPoint)
+	{
+	TInt isForeground = iStdin.IsForeground();
+	if (isForeground < 0)
+		{
+		return isForeground;
+		}
+	return aStdinEndPoint.Attach(iStdin, isForeground ? RIoEndPoint::EForeground : RIoEndPoint::EBackground);
+	}
+
+TInt CCommandWrapperBase::CmndReattachStdout(RIoEndPoint& aStdoutEndPoint)
+	{
+	return aStdoutEndPoint.Attach(iStdout);
+	}
+
+TInt CCommandWrapperBase::CmndReattachStderr(RIoEndPoint& aStderrEndPoint)
+	{
+	return aStderrEndPoint.Attach(iStderr);
+	}
+
+TBool CCommandWrapperBase::CmndIsDisownable() const
+	{
+	return EFalse;
+	}
+
+void CCommandWrapperBase::CmndDisown()
+	{
+	ASSERT(EFalse);
+	}
+
+void CCommandWrapperBase::CmndRelease()
+	{
+	delete this;
+	}
+
+
+//
+// CThreadCommand.
+//
+
+CThreadCommand* CThreadCommand::NewL(const TDesC& aName, TCommandConstructor aCommandConstructor, TUint aFlags)
+	{
+	CThreadCommand* self = new(ELeave) CThreadCommand(aCommandConstructor, aFlags);
+	CleanupStack::PushL(self);
+	self->ConstructL(aName);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CThreadCommand::~CThreadCommand()
+	{
+	delete iWatcher;
+	delete iArgs;
+	iThread.Close();
+	}
+
+CThreadCommand::CThreadCommand(TCommandConstructor aCommandConstructor, TUint aFlags)
+	: iFlags(aFlags), iCommandConstructor(aCommandConstructor)
+	{
+	iThread.SetHandle(0); // By default RThread refers to the current thread. This results in fshell's thread exiting if this object gets killed before it has managed to open a real thread handle.
+	if (iFlags & EUpdateEnvironment) iFlags |= ESharedHeap; // Update environment implies a shared heap, ever since we did away with the explict SwitchAllocator
+	}
+
+void CThreadCommand::ConstructL(const TDesC& aName)
+	{
+	BaseConstructL(aName);
+	iWatcher = CThreadWatcher::NewL();
+	}
+
+void CommandThreadStartL(CThreadCommand::TArgs& aArgs)
+	{
+	if (aArgs.iFlags & CThreadCommand::ESharedHeap)
+		{
+		// If we're sharing the main fshell heap, we have to play by the rules and not crash
+		User::SetCritical(User::EProcessCritical);
+		}
+
+	CActiveScheduler* scheduler = new(ELeave) CActiveScheduler;
+	CleanupStack::PushL(scheduler);
+	CActiveScheduler::Install(scheduler);
+
+	HBufC* commandLine = aArgs.iCommandLine.AllocLC();
+
+	IoUtils::CEnvironment* env;
+	if (aArgs.iFlags & CThreadCommand::EUpdateEnvironment)
+		{
+		env = aArgs.iEnv.CreateSharedEnvironmentL();
+		}
+	else
+		{
+		// A straight-forward copy
+		env = IoUtils::CEnvironment::NewL(aArgs.iEnv);
+		}
+	CleanupStack::PushL(env);
+
+	CCommandBase* command = (*aArgs.iCommandConstructor)();
+	RThread parentThread;
+	User::LeaveIfError(parentThread.Open(aArgs.iParentThreadId));
+	parentThread.RequestComplete(aArgs.iParentStatus, KErrNone);
+	parentThread.Close();
+
+	command->RunCommandL(commandLine, env);
+	CleanupStack::PopAndDestroy(4, scheduler); // env, command, commandline, scheduler
+	}
+
+TInt CommandThreadStart(TAny* aPtr)
+	{
+	CThreadCommand::TArgs args = *(CThreadCommand::TArgs*)aPtr;
+	TBool sharedHeap = (args.iFlags & CThreadCommand::ESharedHeap);
+	if (!sharedHeap)
+		{
+		__UHEAP_MARK;
+		}
+	TInt err = KErrNoMemory;
+	CTrapCleanup* cleanup = CTrapCleanup::New();
+	if (cleanup)
+		{
+		TRAP(err, CommandThreadStartL(args));
+		delete cleanup;
+		}
+	if (!sharedHeap)
+		{
+		__UHEAP_MARKEND;
+		}
+	return err;
+	}
+
+void SetHandleOwnersL(TThreadId aThreadId, RIoReadHandle& aStdin, RIoWriteHandle& aStdout, RIoWriteHandle& aStderr)
+	{
+	User::LeaveIfError(aStdin.SetOwner(aThreadId));
+	User::LeaveIfError(aStdout.SetOwner(aThreadId));
+	User::LeaveIfError(aStderr.SetOwner(aThreadId));
+	};
+
+TInt CThreadCommand::CmndRun(const TDesC& aCommandLine, IoUtils::CEnvironment& aEnv, MCommandObserver& aObserver, RIoSession&)
+	{
+	ASSERT(iObserver == NULL);
+
+	TRequestStatus status(KRequestPending);
+	iArgs = new TArgs(iFlags, aEnv, iCommandConstructor, aCommandLine, status);
+	if (iArgs == NULL)
+		{
+		return KErrNoMemory;
+		}
+
+	TInt i = 0;
+	TName threadName;
+	TInt err = KErrNone;
+	do
+		{
+		const TDesC& name = CmndName();
+		threadName.Format(_L("%S_%02d"), &name, i++);
+		if (iFlags & ESharedHeap)
+			{
+			err = iThread.Create(threadName, CommandThreadStart, KDefaultStackSize, NULL, iArgs);
+			}
+		else
+			{
+			err = iThread.Create(threadName, CommandThreadStart, KDefaultStackSize, KMinHeapSize, KMaxHeapSize, iArgs);
+			}
+		}
+		while (err == KErrAlreadyExists);
+
+	if (err)
+		{
+		return err;
+		}
+
+	err = iWatcher->Logon(*this, iThread, aObserver);
+	if (err)
+		{
+		iThread.Kill(0);
+		iThread.Close();
+		return err;
+		}
+
+	TThreadId threadId = iThread.Id();
+	TRAP(err, SetHandleOwnersL(threadId, CmndStdin(), CmndStdout(), CmndStderr()));
+	if (err)
+		{
+		iThread.Kill(0);
+		iThread.Close();
+		return err;
+		}
+
+	iThread.Resume();
+	User::WaitForRequest(status, iWatcher->iStatus);
+	if (status == KRequestPending)
+		{
+		iThread.Close();
+		return iWatcher->iStatus.Int();
+		}
+
+	iWatcher->SetActive();
+	iObserver = &aObserver;
+	return KErrNone;
+	}
+
+void CThreadCommand::CmndForeground()
+	{
+	iThread.SetPriority(EPriorityAbsoluteForeground);
+	}
+
+void CThreadCommand::CmndBackground()
+	{
+	iThread.SetPriority(EPriorityAbsoluteBackground);
+	}
+
+void CThreadCommand::CmndKill()
+	{
+	if (iThread.Handle())
+		{
+		iThread.Kill(KErrAbort);
+		}
+	}
+
+TInt CThreadCommand::CmndSuspend()
+	{
+	iThread.Suspend();
+	return KErrNone;
+	}
+
+TInt CThreadCommand::CmndResume()
+	{
+	iThread.Resume();
+	return KErrNone;
+	}
+
+TExitType CThreadCommand::CmndExitType() const
+	{
+	return iThread.ExitType();
+	}
+
+
+TExitCategoryName CThreadCommand::CmndExitCategory() const
+	{
+	return iThread.ExitCategory();
+	}
+
+
+//
+// CThreadCommand::TArgs.
+//
+
+CThreadCommand::TArgs::TArgs(TUint aFlags, IoUtils::CEnvironment& aEnv, TCommandConstructor aCommandConstructor, const TDesC& aCommandLine, TRequestStatus& aParentStatus)
+	: iFlags(aFlags), iEnv(aEnv), iCommandConstructor(aCommandConstructor), iCommandLine(aCommandLine), iParentStatus(&aParentStatus), iParentThreadId(RThread().Id())
+	{
+	}
+
+
+//
+// CThreadCommand::CThreadWatcher.
+//
+
+CThreadCommand::CThreadWatcher* CThreadCommand::CThreadWatcher::NewL()
+	{
+	return new(ELeave) CThreadWatcher();
+	}
+
+CThreadCommand::CThreadWatcher::~CThreadWatcher()
+	{
+	Cancel();
+	}
+
+CThreadCommand::CThreadWatcher::CThreadWatcher()
+	: CActive(CActive::EPriorityStandard)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+TInt CThreadCommand::CThreadWatcher::Logon(CThreadCommand& aCommand, RThread& aThread, MCommandObserver& aObserver)
+	{
+	TInt ret = KErrNone;
+	aThread.Logon(iStatus);
+	if (iStatus != KRequestPending)
+		{
+		User::WaitForRequest(iStatus);
+		ret = iStatus.Int();
+		}
+	else
+		{
+		iCommand = &aCommand;
+		iThread = &aThread;
+		iObserver = &aObserver;
+		}
+	return ret;
+	}
+
+void CThreadCommand::CThreadWatcher::SetActive()
+	{
+	CActive::SetActive();
+	}
+
+void CThreadCommand::CThreadWatcher::RunL()
+	{
+	iObserver->HandleCommandComplete(*iCommand, iStatus.Int());
+	}
+
+void CThreadCommand::CThreadWatcher::DoCancel()
+	{
+	if (iThread)
+		{
+		iThread->LogonCancel(iStatus);
+		}
+	}
+
+
+//
+// CProcessCommand.
+//
+
+CProcessCommand* CProcessCommand::NewL(const TDesC& aExeName)
+	{
+	CProcessCommand* self = new(ELeave) CProcessCommand();
+	CleanupStack::PushL(self);
+	self->ConstructL(aExeName);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CProcessCommand* CProcessCommand::NewL(const TDesC& aExeName, RProcess& aProcess)
+	{
+	CProcessCommand* self = new(ELeave) CProcessCommand();
+	CleanupStack::PushL(self);
+	self->ConstructL(aExeName, &aProcess);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+
+CProcessCommand::~CProcessCommand()
+	{
+	delete iWatcher;
+	iProcess.Close();
+	}
+
+CProcessCommand::CProcessCommand()
+	{
+	iProcess.SetHandle(KNullHandle); // By default RProcess refers to the current process (KCurrentProcessHandle). This results in fshell's process exiting if this object gets killed before it has managed to open a real process handle.
+	}
+
+void CProcessCommand::ConstructL(const TDesC& aExeName, RProcess* aProcess)
+	{
+	BaseConstructL(aExeName);
+	iWatcher = CProcessWatcher::NewL();
+	if (aProcess)
+		{
+		// Don't take ownership of the aProcess handle until after everything that can leave has been run
+		iProcess.SetHandle(aProcess->Handle());
+		}
+	}
+
+void CProcessCommand::CreateProcessL(const TDesC& aCommandLine, IoUtils::CEnvironment&)
+	{
+	if (iProcess.Handle() == KNullHandle)
+		{
+		// Don't create new proc if we were passed one in initially
+		User::LeaveIfError(iProcess.Create(CmndName(), aCommandLine));
+		}
+	}
+
+void CProcessCommand::CmndRunL(const TDesC& aCommandLine, IoUtils::CEnvironment& aEnv, MCommandObserver& aObserver)
+	{
+	CreateProcessL(aCommandLine, aEnv);
+
+	HBufC8* envBuf = aEnv.ExternalizeLC();
+	User::LeaveIfError(iProcess.SetParameter(IoUtils::KEnvironmentProcessSlot, *envBuf));
+	CleanupStack::PopAndDestroy(envBuf);
+
+	TFullName fullName(iProcess.Name());
+	_LIT(KThreadName,"::Main");
+	fullName.Append(KThreadName);
+	RThread thread;
+	User::LeaveIfError(thread.Open(fullName));
+	TThreadId threadId(thread.Id());
+	thread.Close();
+	User::LeaveIfError(CmndStdin().SetOwner(threadId));
+	User::LeaveIfError(CmndStdout().SetOwner(threadId));
+	User::LeaveIfError(CmndStderr().SetOwner(threadId));
+	TInt err = iWatcher->Logon(*this, iProcess, aObserver);
+	if (err)
+		{
+		iProcess.Kill(0);
+		}
+	User::LeaveIfError(err);
+	iObserver = &aObserver;
+	iProcess.Resume();
+	}
+
+TInt CProcessCommand::CmndRun(const TDesC& aCommandLine, IoUtils::CEnvironment& aEnv, MCommandObserver& aObserver, RIoSession&)
+	{
+	ASSERT(iObserver == NULL);
+	TRAPD(err, CmndRunL(aCommandLine, aEnv, aObserver));
+	return err;
+	}
+
+void CProcessCommand::CmndForeground()
+	{
+	iProcess.SetPriority(EPriorityForeground);
+	}
+
+void CProcessCommand::CmndBackground()
+	{
+	iProcess.SetPriority(EPriorityBackground);
+	}
+
+void CProcessCommand::CmndKill()
+	{
+	if (iProcess.Handle())
+		{
+		iProcess.Kill(KErrAbort);
+		}
+	}
+
+TInt CProcessCommand::CmndSuspend()
+	{
+#ifdef EKA2
+	// Can't currently support suspend in EKA2 - KERN-EXEC 46 will result.
+	return KErrNotSupported;
+#else
+	TName processName(iProcess.Name());
+	processName.Append(_L("::*"));
+	TFullName threadName;
+	TFindThread threadFinder(processName);
+	while (threadFinder.Next(threadName) == KErrNone)
+		{
+		RThread thread;
+		if (thread.Open(threadName) == KErrNone)
+			{
+			thread.Suspend();
+			thread.Close();
+			}
+		}
+	return KErrNone;
+#endif
+	}
+
+TInt CProcessCommand::CmndResume()
+	{
+#ifdef EKA2
+	// Can't currently support resume in EKA2 - KERN-EXEC 46 will result.
+	return KErrNotSupported;
+#else
+	TName processName(iProcess.Name());
+	processName.Append(_L("::*"));
+	TFullName threadName;
+	TFindThread threadFinder(processName);
+	while (threadFinder.Next(threadName) == KErrNone)
+		{
+		RThread thread;
+		if (thread.Open(threadName) == KErrNone)
+			{
+			thread.Resume();
+			thread.Close();
+			}
+		}
+	return KErrNone;
+#endif
+	}
+
+TExitType CProcessCommand::CmndExitType() const
+	{
+	return iProcess.ExitType();
+	}
+
+TExitCategoryName CProcessCommand::CmndExitCategory() const
+	{
+	return iProcess.ExitCategory();
+	}
+
+TBool CProcessCommand::CmndIsDisownable() const
+	{
+	return ETrue;
+	}
+
+void CProcessCommand::CmndDisown()
+	{
+	delete iWatcher;
+	iWatcher = NULL;
+	iProcess.Close();
+	}
+
+
+//
+// CProcessCommand::CProcessWatcher.
+//
+
+CProcessCommand::CProcessWatcher* CProcessCommand::CProcessWatcher::NewL()
+	{
+	return new(ELeave) CProcessWatcher();
+	}
+
+CProcessCommand::CProcessWatcher::~CProcessWatcher()
+	{
+	Cancel();
+	}
+
+TInt CProcessCommand::CProcessWatcher::Logon(CProcessCommand& aCommand, RProcess& aProcess, MCommandObserver& aObserver)
+	{
+	TInt ret = KErrNone;
+	aProcess.Logon(iStatus);
+	if (iStatus != KRequestPending)
+		{
+		User::WaitForRequest(iStatus);
+		ret = iStatus.Int();
+		}
+	else
+		{
+		iCommand = &aCommand;
+		iProcess = &aProcess;
+		iObserver = &aObserver;
+		SetActive();
+		}
+	return ret;
+	}
+
+CProcessCommand::CProcessWatcher::CProcessWatcher()
+	: CActive(CActive::EPriorityStandard)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+void CProcessCommand::CProcessWatcher::RunL()
+	{
+	iObserver->HandleCommandComplete(*iCommand, iStatus.Int());
+	}
+
+void CProcessCommand::CProcessWatcher::DoCancel()
+	{
+	if (iProcess)
+		{
+		iProcess->LogonCancel(iStatus);
+		}
+	}
+
+//
+// CPipsCommand.
+//
+
+CPipsCommand* CPipsCommand::NewL(const TDesC& aExeName)
+	{
+	CPipsCommand* self = new (ELeave) CPipsCommand();
+	CleanupStack::PushL(self);
+	self->ConstructL(aExeName);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CPipsCommand::CPipsCommand()
+	{
+	}
+
+CPipsCommand::~CPipsCommand()
+	{
+	}
+
+TInt CPipsCommand::CmndRun(const TDesC& aCommandLine, IoUtils::CEnvironment& aEnv, MCommandObserver& aObserver, RIoSession& aIoSession)
+	{
+	TInt err = CProcessCommand::CmndRun(aCommandLine, aEnv, aObserver, aIoSession);
+	if ((err == KErrNone) && iUsingPipsRun)
+		{
+		TRequestStatus status;
+		iProcess.Rendezvous(status);
+		User::WaitForRequest(status);
+		err = status.Int();
+		if (err > 0)
+			{
+			iPipsRunChildProcessId = err;
+			err = KErrNone;
+			}
+		}
+	return err;
+	}
+
+void CPipsCommand::CmndKill()
+	{
+	CProcessCommand::CmndKill();
+
+	if (iUsingPipsRun)
+		{
+		RProcess pipsRunChildProcess;
+		if (pipsRunChildProcess.Open(iPipsRunChildProcessId) == KErrNone)
+			{
+			pipsRunChildProcess.Kill(KErrAbort);
+			pipsRunChildProcess.Close();
+			}
+		}
+	}
+
+void CPipsCommand::CreateProcessL(const TDesC& aCommandLine, IoUtils::CEnvironment&)
+	{
+	_LIT(KPipsRunExe, "pipsrun");
+	TInt err = iProcess.Create(KPipsRunExe, KNullDesC);
+	if (err == KErrNotFound)
+		{
+		// Looks like pipsrun.exe isn't present - just load the PIPS exe directly.
+		User::LeaveIfError(iProcess.Create(CmndName(), aCommandLine));
+		}
+	else
+		{
+		User::LeaveIfError(err);
+		User::LeaveIfError(iProcess.SetParameter(IoUtils::KPipsCommandNameProcessSlot, CmndName()));
+		User::LeaveIfError(iProcess.SetParameter(IoUtils::KPipsCommandLineProcessSlot, aCommandLine));
+		iUsingPipsRun = ETrue;
+		}
+	}
+
+
+//
+// CAliasCommand.
+//
+
+CAliasCommand* CAliasCommand::NewL(MCommand& aAliasedCommand, const TDesC* aAdditionalArguments, const TDesC* aReplacementArguments)
+	{
+	CAliasCommand* self = new(ELeave) CAliasCommand(aAliasedCommand);
+	CleanupStack::PushL(self);
+	self->ConstructL(aAdditionalArguments, aReplacementArguments);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CAliasCommand::~CAliasCommand()
+	{
+	delete iAdditionalArguments;
+	delete iReplacementArguments;
+	iAliasedCommand.CmndRelease();
+	}
+
+CAliasCommand::CAliasCommand(MCommand& aAliasedCommand)
+	: iAliasedCommand(aAliasedCommand)
+	{
+	}
+
+void CAliasCommand::ConstructL(const TDesC* aAdditionalArguments, const TDesC* aReplacementArguments)
+	{
+	BaseConstructL(iAliasedCommand.CmndName());
+	if (aAdditionalArguments)
+		{
+		iAdditionalArguments = aAdditionalArguments->AllocL();
+		}
+	if (aReplacementArguments)
+		{
+		iReplacementArguments = aReplacementArguments->AllocL();
+		}
+	}
+
+RIoReadHandle& CAliasCommand::CmndStdin()
+	{
+	return iAliasedCommand.CmndStdin();
+	}
+
+RIoWriteHandle& CAliasCommand::CmndStdout()
+	{
+	return iAliasedCommand.CmndStdout();
+	}
+
+RIoWriteHandle& CAliasCommand::CmndStderr()
+	{
+	return iAliasedCommand.CmndStderr();
+	}
+
+TInt CAliasCommand::CmndRun(const TDesC& aCommandLine, IoUtils::CEnvironment& aEnv, MCommandObserver& aObserver, RIoSession& aIoSession)
+	{
+	if (iAdditionalArguments && !iReplacementArguments)
+		{
+		iAdditionalArguments = iAdditionalArguments->ReAlloc(iAdditionalArguments->Length() + aCommandLine.Length() + 1);
+		if (iAdditionalArguments == NULL)
+			{
+			return KErrNoMemory;
+			}
+		_LIT(KSpace, " ");
+		iAdditionalArguments->Des().Append(KSpace);
+		iAdditionalArguments->Des().Append(aCommandLine);
+		}
+	iCommandObserver = &aObserver;
+	const TDesC* args = &aCommandLine;
+	if (iReplacementArguments)
+		{
+		args = iReplacementArguments;
+		}
+	else if (iAdditionalArguments)
+		{
+		args = iAdditionalArguments;
+		}
+	return iAliasedCommand.CmndRun(*args, aEnv, *this, aIoSession);
+	}
+
+void CAliasCommand::CmndForeground()
+	{
+	iAliasedCommand.CmndForeground();
+	}
+
+void CAliasCommand::CmndBackground()
+	{
+	iAliasedCommand.CmndBackground();
+	}
+
+void CAliasCommand::CmndKill()
+	{
+	iAliasedCommand.CmndKill();
+	}
+
+TInt CAliasCommand::CmndSuspend()
+	{
+	return iAliasedCommand.CmndSuspend();
+	}
+
+TInt CAliasCommand::CmndResume()
+	{
+	return iAliasedCommand.CmndResume();
+	}
+
+TExitType CAliasCommand::CmndExitType() const
+	{
+	return iAliasedCommand.CmndExitType();
+	}
+
+TExitCategoryName CAliasCommand::CmndExitCategory() const
+	{
+	return iAliasedCommand.CmndExitCategory();
+	}
+
+void CAliasCommand::HandleCommandComplete(MCommand&, TInt aError)
+	{
+	iCommandObserver->HandleCommandComplete(*this, aError);
+	}