--- /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);
+ }