diff -r 000000000000 -r 7f656887cf89 core/src/command_wrappers.cpp --- /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); + }