author | Tom Sutcliffe <thomas.sutcliffe@accenture.com> |
Thu, 05 Aug 2010 12:06:56 +0100 | |
changeset 25 | 482757737e59 |
parent 0 | 7f656887cf89 |
child 36 | 99de8c43cede |
child 72 | c9dfb364c2d1 |
permissions | -rw-r--r-- |
// 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); }