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
{
return mProcess.pid;
}
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());
return NULL;
}
error.clear();
return ppipe;
}
template <class PIPETYPE>
PIPETYPE& LLProcess::getPipe(FILESLOT slot)
{
std::string error;
PIPETYPE* wp = getPipePtr<PIPETYPE>(error, slot);
if (! wp)
{
LLTHROW(NoPipe(error));
}
return *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)
{
return getPipe<ReadPipe>(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);
}
return out;
}
/*****************************************************************************
* Windows specific
*****************************************************************************/
#if LL_WINDOWS
static std::string WindowsErrorString(const std::string& operation);
void LLProcess::autokill()
// 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.
if (! desc.empty())
{
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;
}
CloseHandle(h);
return 0;
}
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;
}
else
{
result.mState = LLProcess::EXITED;
}
result.mData = status;
return result;
/// GetLastError()/FormatMessage() boilerplate
static std::string WindowsErrorString(const std::string& operation)
{
auto result = GetLastError();
return STRINGIZE(operation << " failed (" << result << "): "
<< windows_message<std::string>(result));
}
/*****************************************************************************
* Posix specific
*****************************************************************************/
#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);
if (wait_result == pid)
{
*pstatus = interpret_status(status);
return true;
}
if (wait_result == 0)
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.
if (! desc.empty())
{
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;
return 0;
}
return pid;
}
static LLProcess::Status interpret_status(int status)
LLProcess::Status result;
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
}
return result;
#endif // Posix