Added ENotifyKeypresses and ECaptureCtrlC flags to CCommandBase.
Commands can now get keypresses and handle ctrl-C via callbacks instead of having to implement custom active objects. As part of this extended the CCommandBase extension interface to MCommandExtensionsV2 for the new virtual functions KeyPressed(TUint aKeyCode, TUint aModifiers) and CtrlCPressed(). sudo now cleans up correctly by using ECaptureCtrlC.
// 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);
}