core/extra/pipsrun/pipsrun.cpp
changeset 0 7f656887cf89
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/extra/pipsrun/pipsrun.cpp	Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,227 @@
+// pipsrun.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/ioutils.h>
+#include <fshell/ltkutils.h>
+#include <stdapis/unistd.h>
+#include <stdapis/stdio.h>
+#include <stdapis/pthread.h>
+
+const TInt KBlockSize = 512;
+
+using namespace IoUtils;
+
+class CCmdPipsRun : public CCommandBase
+	{
+public:
+	static CCommandBase* NewLC();
+	~CCmdPipsRun();
+private:
+	CCmdPipsRun();
+private: // From CCommandBase.
+	virtual const TDesC& Name() const;
+	virtual const TDesC& Description() const;
+	virtual void DoRunL();
+	};
+
+
+CCommandBase* CCmdPipsRun::NewLC()
+	{
+	CCmdPipsRun* self = new(ELeave) CCmdPipsRun();
+	CleanupStack::PushL(self);
+	self->BaseConstructL();
+	return self;
+	}
+
+CCmdPipsRun::~CCmdPipsRun()
+	{
+	}
+
+CCmdPipsRun::CCmdPipsRun() : CCommandBase(ESharableIoSession)
+	{
+	}
+
+const TDesC& CCmdPipsRun::Name() const
+	{
+	_LIT(KName, "pipsrun");	
+	return KName;
+	}
+
+const TDesC& CCmdPipsRun::Description() const
+	{
+	_LIT(KDescription, "Internal helper executable for launching processes that use PIPS in such a way that they can interact with fshell's I/O primitives. You should not call it directly.");
+	return KDescription;
+	}
+
+const TUint8* GetDesParamLC(TInt aSlot)
+	{
+	TInt paramLen = User::ParameterLength(aSlot);
+	StaticLeaveIfErr(paramLen, _L("Couldn't read environment slot %d"), aSlot);
+	HBufC* buf = HBufC::NewLC(paramLen + 1); // + 1 for the terminating null character.
+	TPtr bufPtr(buf->Des());
+	User::LeaveIfError(User::GetDesParameter(aSlot, bufPtr));
+	TPtr8 narrowPtr = buf->Des().Collapse();
+	narrowPtr.ZeroTerminate();
+	return narrowPtr.Ptr();
+	}
+
+void AppendNarrowDes(TUint8*& aTarget, const TDesC& aDes)
+	{
+	TInt len = aDes.Length();
+	const TUint16 *pS = aDes.Ptr();
+	const TUint16 *pE = pS + len;
+	while (pS < pE)
+		{
+		TUint c = (*pS++);
+		if (c >= 0x100)
+			{
+			c = 1;
+			}
+		*aTarget++ = (TUint8)c;
+		}
+	}
+
+class TGlueParams
+	{
+public:
+	TGlueParams(TInt aFileDescriptor, RIoWriteHandle aWriteHandle);
+public:
+	TInt iFileDescriptor;
+	RIoWriteHandle iWriteHandle;
+	};
+
+TGlueParams::TGlueParams(TInt aFileDescriptor, RIoWriteHandle aWriteHandle)
+	: iFileDescriptor(aFileDescriptor), iWriteHandle(aWriteHandle)
+	{
+	}
+
+void* GlueFileDescriptor(void* aParams)
+	{
+	TGlueParams* params = (TGlueParams*)aParams;
+	TBuf8<KBlockSize * 2> buf;
+	ssize_t s;
+	TInt err = KErrNone;
+	do
+		{
+		s = read((int)params->iFileDescriptor, (char*)buf.Ptr(), KBlockSize);
+		if (s > 0)
+			{
+			buf.SetLength(s);
+			err = params->iWriteHandle.Write(buf.Expand());
+			}
+		}
+		while ((s > 0) && (err == KErrNone));
+	return (void*)err;
+	}
+
+void CCmdPipsRun::DoRunL()
+	{
+	// Pack the envronment object we inherited from fshell in the way POSIX expects.
+	_LIT(KEquals, "=");
+	_LIT(KNull, "\x0");
+	const CEnvironment& env = Env();
+	RPointerArray<HBufC> keys;
+	LtkUtils::CleanupResetAndDestroyPushL(keys);
+	env.GetKeysL(keys);
+	const TInt numVars = keys.Count();
+	TUint8** envp = (TUint8**)User::AllocLC((numVars  + 1) * sizeof(TInt*)); // + 1 because the last pointer must be NULL.
+	for (TInt i = 0; i < numVars; ++i)
+		{
+		const TDesC& key = *keys[i];
+		const TDesC& val = env.GetAsDesL(key);
+		const TInt length = (key.Length() + 2 + val.Length()); // 2 for the delimiting '=' and the terminating null.
+		TUint8* p = (TUint8*)User::AllocLC(length);
+		envp[i] = p;
+		AppendNarrowDes(p, key);
+		AppendNarrowDes(p, KEquals);
+		AppendNarrowDes(p, val);
+		AppendNarrowDes(p, KNull);
+		}
+	envp[numVars] = NULL;
+
+	const TUint8* commandName(GetDesParamLC(IoUtils::KPipsCommandNameProcessSlot));
+	const TUint8* commandLine(GetDesParamLC(IoUtils::KPipsCommandLineProcessSlot));
+	TInt fds[3];
+	TInt pid = popen3((const char*)commandName, (const char*)commandLine, (char**)envp, fds);
+	if (pid == -1)
+		{
+		User::Leave(KErrGeneral);
+		}
+	CleanupStack::PopAndDestroy(2 + numVars + 1);
+	CleanupStack::PopAndDestroy(&keys);
+
+	RProcess childProcess;
+	User::LeaveIfError(childProcess.Open(pid));
+	CleanupClosePushL(childProcess);
+	TRequestStatus childProcessStatus;
+	childProcess.Logon(childProcessStatus);
+	RProcess::Rendezvous(pid);
+
+	pthread_t stdoutReaderThreadId = 0;
+	pthread_t stderrReaderThreadId = 0;
+	pthread_attr_t threadAttr;
+	pthread_attr_init(&threadAttr);
+	pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
+	TGlueParams stdoutParams(fds[1], Stdout());
+	TGlueParams stderrParams(fds[2], Stderr());
+	TInt err = pthread_create(&stdoutReaderThreadId, &threadAttr, GlueFileDescriptor, (void*)&stdoutParams);
+	if (err == 0)
+		{
+		err = pthread_create(&stderrReaderThreadId, &threadAttr, GlueFileDescriptor, (void*)&stderrParams);
+		}
+	if (err != 0)
+		{
+		User::Leave(err);
+		}
+
+	Stdin().SetReadMode(RIoReadHandle::EOneOrMore);
+	TBuf<KBlockSize> buf;
+	TRequestStatus stdinReadStatus;
+	TBool finished(EFalse);
+	while (!finished)
+		{
+		Stdin().Read(buf, stdinReadStatus);
+		User::WaitForRequest(childProcessStatus, stdinReadStatus);
+		if (childProcessStatus == KRequestPending)
+			{
+			TPtr8 narrowPtr(buf.Collapse());
+			narrowPtr.ZeroTerminate();			
+			do
+				{
+				TInt ret = write(fds[0], narrowPtr.Ptr(), narrowPtr.Length());
+				if (ret < 0)
+					{
+					finished = ETrue;
+					}
+				else
+					{
+					narrowPtr.Set(narrowPtr.MidTPtr(ret));
+					}
+				}
+				while (!finished && narrowPtr.Length());
+			}
+		else
+			{
+			finished = ETrue;
+			Stdin().ReadCancel();
+			User::WaitForRequest(stdinReadStatus);
+			}
+		}
+
+	CleanupStack::PopAndDestroy(&childProcess);
+	pthread_join(stdoutReaderThreadId, NULL);
+	pthread_join(stderrReaderThreadId, NULL);
+	Complete(childProcessStatus.Int());
+	}
+
+EXE_BOILER_PLATE(CCmdPipsRun)
+