Skip to content
Snippets Groups Projects
llprocess.cpp 44.3 KiB
Newer Older

	// If caller requested notification on child termination, send it.
	if (! mPostend.empty())
	{
		LLEventPumps::instance().obtain(mPostend)
			.post(LLSDMap
				  ("id",     getProcessID())
				  ("desc",   mDesc)
				  ("state",  mStatus.mState)
				  ("data",   mStatus.mData)
				  ("string", getStatusString())
				 );
	}
}

LLProcess::id LLProcess::getProcessID() const
{
}

LLProcess::handle LLProcess::getProcessHandle() const
{
#if LL_WINDOWS
	return mProcess.hproc;
#else
	return mProcess.pid;
#endif
std::string LLProcess::getPipeName(FILESLOT) const
{
	// LLProcess::FileParam::type "npipe" is not yet implemented
	return "";
}

template<class PIPETYPE>
PIPETYPE* LLProcess::getPipePtr(std::string& error, FILESLOT slot)
{
	if (slot >= NSLOTS)
	{
		error = STRINGIZE(mDesc << " has no slot " << slot);
		return NULL;
	}
	if (mPipes.is_null(slot))
	{
		error = STRINGIZE(mDesc << ' ' << whichfile(slot) << " not a monitored pipe");
		return NULL;
	}
	// Make sure we dynamic_cast in pointer domain so we can test, rather than
	// accepting runtime's exception.
	PIPETYPE* ppipe = dynamic_cast<PIPETYPE*>(&mPipes[slot]);
	if (! ppipe)
	{
		error = STRINGIZE(mDesc << ' ' << whichfile(slot) << " not a " << typeid(PIPETYPE).name());
}

template <class PIPETYPE>
PIPETYPE& LLProcess::getPipe(FILESLOT slot)
{
	std::string error;
	PIPETYPE* wp = getPipePtr<PIPETYPE>(error, slot);
	if (! wp)
	{
}

template <class PIPETYPE>
boost::optional<PIPETYPE&> LLProcess::getOptPipe(FILESLOT slot)
{
	std::string error;
	PIPETYPE* wp = getPipePtr<PIPETYPE>(error, slot);
	if (! wp)
	{
		LL_DEBUGS("LLProcess") << error << LL_ENDL;
		return boost::optional<PIPETYPE&>();
	}
	return *wp;
}

LLProcess::WritePipe& LLProcess::getWritePipe(FILESLOT slot)
{
	return getPipe<WritePipe>(slot);
}

boost::optional<LLProcess::WritePipe&> LLProcess::getOptWritePipe(FILESLOT slot)
{
	return getOptPipe<WritePipe>(slot);
}

LLProcess::ReadPipe& LLProcess::getReadPipe(FILESLOT slot)
{
}

boost::optional<LLProcess::ReadPipe&> LLProcess::getOptReadPipe(FILESLOT slot)
{
	return getOptPipe<ReadPipe>(slot);
//static
std::string LLProcess::getline(std::istream& in)
{
	std::string line;
	std::getline(in, line);
	// Blur the distinction between "\r\n" and plain "\n". std::getline() will
	// have eaten the "\n", but we could still end up with a trailing "\r".
	std::string::size_type lastpos = line.find_last_not_of("\r");
	if (lastpos != std::string::npos)
	{
		// Found at least one character that's not a trailing '\r'. SKIP OVER
		// IT and erase the rest of the line.
		line.erase(lastpos+1);
	}
	return line;
}

std::ostream& operator<<(std::ostream& out, const LLProcess::Params& params)
{
	if (params.cwd.isProvided())
		out << "cd " << LLStringUtil::quote(params.cwd) << ": ";
	out << LLStringUtil::quote(params.executable);
	for(const std::string& arg : params.args)
		out << ' ' << LLStringUtil::quote(arg);
/*****************************************************************************
*   Windows specific
*****************************************************************************/
static std::string WindowsErrorString(const std::string& operation);

	// hopefully now handled by apr_procattr_autokill_set()
LLProcess::handle LLProcess::isRunning(handle h, const std::string& desc)
	// This direct Windows implementation is because we have no access to the
	// apr_proc_t struct: we expect it's been destroyed.
	DWORD waitresult = WaitForSingleObject(h, 0);
	if(waitresult == WAIT_OBJECT_0)
	{
		// the process has completed.
			DWORD status = 0;
			if (! GetExitCodeProcess(h, &status))
			{
				LL_WARNS("LLProcess") << desc << " terminated, but "
									  << WindowsErrorString("GetExitCodeProcess()") << LL_ENDL;
			}
			{
				LL_INFOS("LLProcess") << getStatusString(desc, interpret_status(status))
									  << LL_ENDL;
			}
static LLProcess::Status interpret_status(int status)
	LLProcess::Status result;

	// This bit of code is cribbed from apr/threadproc/win32/proc.c, a
	// function (unfortunately static) called why_from_exit_code():
	/* See WinNT.h STATUS_ACCESS_VIOLATION and family for how
	 * this class of failures was determined
	 */
	if ((status & 0xFFFF0000) == 0xC0000000)
	{
		result.mState = LLProcess::KILLED;
		result.mState = LLProcess::EXITED;
/// GetLastError()/FormatMessage() boilerplate
static std::string WindowsErrorString(const std::string& operation)
{
	auto result = GetLastError();
	return STRINGIZE(operation << " failed (" << result << "): "
					 << windows_message<std::string>(result));
}

/*****************************************************************************
*****************************************************************************/
#else // Mac and linux

#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>

void LLProcess::autokill()
{
	// What we ought to do here is to:
	// 1. create a unique process group and run all autokill children in that
	//    group (see https://jira.secondlife.com/browse/SWAT-563);
	// 2. figure out a way to intercept control when the viewer exits --
	//    gracefully or not; 
	// 3. when the viewer exits, kill off the aforementioned process group.

	// It's point 2 that's troublesome. Although I've seen some signal-
	// handling logic in the Posix viewer code, I haven't yet found any bit of
	// code that's run no matter how the viewer exits (a try/finally for the
	// whole process, as it were).
}

// Attempt to reap a process ID -- returns true if the process has exited and been reaped, false otherwise.
static bool reap_pid(pid_t pid, LLProcess::Status* pstatus=NULL)
	LLProcess::Status dummy;
	if (! pstatus)
	{
		// If caller doesn't want to see Status, give us a target anyway so we
		// don't have to have a bunch of conditionals.
		pstatus = &dummy;
	}

	int status = 0;
	pid_t wait_result = ::waitpid(pid, &status, WNOHANG);
		*pstatus = interpret_status(status);
		pstatus->mState = LLProcess::RUNNING;
		pstatus->mData	= 0;
		return false;
	// Clear caller's Status block; caller must interpret UNSTARTED to mean
	// "if this PID was ever valid, it no longer is."
	*pstatus = LLProcess::Status();
	// We've dealt with the success cases: we were able to reap the child
	// (wait_result == pid) or it's still running (wait_result == 0). It may
	// be that the child terminated but didn't hang around long enough for us
	// to reap. In that case we still have no Status to report, but we can at
	// least state that it's not running.
	if (wait_result == -1 && errno == ECHILD)
		// No such process -- this may mean we're ignoring SIGCHILD.
		return true;
	// Uh, should never happen?!
	LL_WARNS("LLProcess") << "LLProcess::reap_pid(): waitpid(" << pid << ") returned "
						  << wait_result << "; not meaningful?" << LL_ENDL;
	// If caller is looping until this pid terminates, and if we can't find
	// out, better to break the loop than to claim it's still running.
	return true;
LLProcess::id LLProcess::isRunning(id pid, const std::string& desc)
	// This direct Posix implementation is because we have no access to the
	// apr_proc_t struct: we expect it's been destroyed.
	if (! pid)
		return 0;

	// Check whether the process has exited, and reap it if it has.
	LLProcess::Status status;
	if(reap_pid(pid, &status))
	{
		// the process has exited.
			std::string statstr(desc + " apparently terminated: no status available");
			// We don't just pass UNSTARTED to getStatusString() because, in
			// the context of reap_pid(), that state has special meaning.
			if (status.mState != UNSTARTED)
			{
				statstr = getStatusString(desc, status);
			}
			LL_INFOS("LLProcess") << statstr << LL_ENDL;
static LLProcess::Status interpret_status(int status)
	if (WIFEXITED(status))
	{
		result.mState = LLProcess::EXITED;
		result.mData  = WEXITSTATUS(status);
	}
	else if (WIFSIGNALED(status))
	{
		result.mState = LLProcess::KILLED;
		result.mData  = WTERMSIG(status);
	}
	else                            // uh, shouldn't happen?
	{
		result.mState = LLProcess::EXITED;
		result.mData  = status;     // someone else will have to decode
	}