commands/setpriority/setpriority.cpp
author Tom Sutcliffe <thomas.sutcliffe@accenture.com>
Thu, 26 Aug 2010 00:49:35 +0100
changeset 37 534b01198c2d
parent 0 7f656887cf89
permissions -rw-r--r--
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.

// setpriority.cpp
// 
// Copyright (c) 2008 - 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 <fshell/memoryaccesscmd.h>

using namespace IoUtils;

class CCmdSetpriority;
class CNewThreadNotifier : public CActive
	{
public:
	CNewThreadNotifier(CCmdSetpriority& aCmd, RMemoryAccess& aMemAccess);
	void Start();
	~CNewThreadNotifier();
	void DoCancel();
	void RunL();

private:
	CCmdSetpriority& iCmd;
	RMemoryAccess& iMemAccess;
	};

class CCmdSetpriority : public CMemoryAccessCommandBase
	{
public:
	static CCommandBase* NewLC();
	~CCmdSetpriority();
	void NewThread(TInt aHandle);
private:
	CCmdSetpriority();
private: // From CCommandBase.
	virtual const TDesC& Name() const;
	virtual void DoRunL();
	virtual void ArgumentsL(RCommandArgumentList& aArguments);
	virtual void OptionsL(RCommandOptionList& aOptions);
private:
	RArray<TUint> iTids;
	RArray<TUint> iPids;
	TInt iPriority;
	HBufC* iName;
	TProcessKernelInfo iProcInfo;
	TThreadKernelInfo iThreadInfo;
	CNewThreadNotifier* iNotifier;
	};


CCommandBase* CCmdSetpriority::NewLC()
	{
	CCmdSetpriority* self = new(ELeave) CCmdSetpriority();
	CleanupStack::PushL(self);
	self->BaseConstructL();
	return self;
	}

CCmdSetpriority::~CCmdSetpriority()
	{
	delete iNotifier;
	delete iName;
	iTids.Close();
	iPids.Close();
	}

CCmdSetpriority::CCmdSetpriority()
	: CMemoryAccessCommandBase(EManualComplete)
	{
	}

const TDesC& CCmdSetpriority::Name() const
	{
	_LIT(KName, "setpriority");
	return KName;
	}

void CCmdSetpriority::ArgumentsL(RCommandArgumentList& aArguments)
	{
	aArguments.AppendIntL(iPriority, _L("priority"));
	}

void CCmdSetpriority::OptionsL(RCommandOptionList& aOptions)
	{
	aOptions.AppendStringL(iName, _L("match"));
	aOptions.AppendUintL(iPids, _L("pid"));
	aOptions.AppendUintL(iTids, _L("tid"));
	}


EXE_BOILER_PLATE(CCmdSetpriority)

void CCmdSetpriority::DoRunL()
	{
	LoadMemoryAccessL();
	TInt err = KErrNone;

	// Fix up priorities that we had to make different to kernel cos fshell can't handle negative arguments
	// See enum TThrdPriority in kern_priv.h for the meaning of these negative numbers
	switch(iPriority)
		{
		case 101: iPriority = -8; break;
		case 102: iPriority = -7; break;
		case 103: iPriority = -6; break;
		case 104: iPriority = -5; break;
		case 105: iPriority = -4; break;
		case 106: iPriority = -3; break;
		case 107: iPriority = -2; break;
		default:
			break;
		}

	if (iName)
		{
		TPtrC8 name8 = iName->Des().Collapse();
		LeaveIfErr(iMemAccess.SetPriorityOverride(iPriority, name8), _L("Couldn't set priority override"));
		iNotifier = new(ELeave) CNewThreadNotifier(*this, iMemAccess);
		iNotifier->Start();
		return;
		}
	
	if (iTids.Count() && iPids.Count())
		{
		LeaveIfErr(KErrArgument, _L("You cannot specify both --tid and --pid - a single priority cannot be valid for thread and process."));
		}

	for (TInt i = 0; i < iTids.Count(); i++)
		{
		TUint id = iTids[i];
		RThread thread;
		err = iMemAccess.RThreadForceOpen(thread, id);
		if (!err) err = iMemAccess.SetThreadPriority(thread, iPriority);
		if (err) PrintError(err, _L("Couldn't set priority for thread %u"), id);
		thread.Close();
		}
	
	for (TInt i = 0; i < iPids.Count(); i++)
		{
		TUint id = iPids[i];
		// Get KProcessFlagPriorityControl flag
		RProcess process;
		err = process.Open(id);
		LeaveIfErr(err, _L("Couldn't open process with ID %u"), id);

		TBool priorityControlAllowed = EFalse;
		TPckg<TProcessKernelInfo> pkg(iProcInfo);
		err = iMemAccess.GetObjectInfoByHandle(EProcess, RThread().Id(), process.Handle(), pkg);
		if (err)
			{
			PrintWarning(_L("Couldn't get priority control flag setting (%d)\r\n"), err);
			}
		else
			{
			if (iProcInfo.iFlags & KProcessFlagPriorityControl) priorityControlAllowed = ETrue;
			}

		if (!priorityControlAllowed)
			{
			PrintError(KErrAccessDenied, _L("This process does not allow setting of its priority"));
			}
		else if (iPriority != EPriorityBackground && iPriority != EPriorityForeground)
			{
			PrintError(KErrAccessDenied, _L("The kernel will ignore requests to set a process priority that isn't Background (250) or Foreground (350). I can't see how to get round this even using memory access. Sorry."));
			}
		process.SetPriority((TProcessPriority)iPriority);
		process.Close();
		}
	Complete(KErrNone);
	}

void CCmdSetpriority::NewThread(TInt aHandle)
	{
	if (aHandle == KErrNotSupported)
		{
		PrintError(KErrNotSupported, _L("Kernel was not compiled with __DEBUGGER_SUPPORT__, can't get thread creation notifications."));
		Complete(aHandle);
		}
	else if (aHandle < 0)
		{
		PrintError(aHandle, _L("Failed to get new thread notification, or couldn't open handle to thread."));
		Complete(aHandle);
		}
	else
		{
		RThread thread;
		thread.SetHandle(aHandle);
		TFullName name(thread.FullName());
		TInt priority = 0;
		TPckg<TThreadKernelInfo> pkg(iThreadInfo);
		TInt err = iMemAccess.GetObjectInfoByHandle(EThread, RThread().Id(), aHandle, pkg);
		if (!err)
			{
			priority = iThreadInfo.iThreadPriority;
			}

		TUint tid = thread.Id();
		Printf(_L("New thread id %u name %S priority adjusted to %d\r\n"), tid, &name, priority);
		thread.Close();
		}
	}

CNewThreadNotifier::CNewThreadNotifier(CCmdSetpriority& aCmd, RMemoryAccess& aMemAccess)
	: CActive(CActive::EPriorityStandard), iCmd(aCmd), iMemAccess(aMemAccess)
	{
	CActiveScheduler::Add(this);
	}

CNewThreadNotifier::~CNewThreadNotifier()
	{
	Cancel();
	}

void CNewThreadNotifier::DoCancel()
	{
	iMemAccess.CancelNotifyThreadCreation();
	}

void CNewThreadNotifier::RunL()
	{
	TInt stat = iStatus.Int();
	if (stat >= 0)
		{
		Start();
		}
	iCmd.NewThread(stat);
	}

void CNewThreadNotifier::Start()
	{
	if (!IsActive())
		{
		iMemAccess.NotifyThreadCreation(iStatus);
		SetActive();
		}
	}