diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 2c376bb016c63dd38415670bc6c06fc7a4b4358e..e2af7265aa399ba73f6da993b2e55db9dd78c927 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -74,7 +74,7 @@ set(llcommon_SOURCE_FILES
     llmortician.cpp
     lloptioninterface.cpp
     llptrto.cpp 
-    llprocesslauncher.cpp
+    llprocess.cpp
     llprocessor.cpp
     llqueuedthread.cpp
     llrand.cpp
@@ -197,7 +197,7 @@ set(llcommon_HEADER_FILES
     llpointer.h
     llpreprocessor.h
     llpriqueuemap.h
-    llprocesslauncher.h
+    llprocess.h
     llprocessor.h
     llptrskiplist.h
     llptrskipmap.h
@@ -328,7 +328,7 @@ if (LL_TESTS)
   LL_ADD_INTEGRATION_TEST(reflection "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
-  LL_ADD_INTEGRATION_TEST(llprocesslauncher "" "${test_libs}"
+  LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}"
                           "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/tests/setpython.py")
   LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")
 
diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8c0caca68045b54ddf1838b51d878092f8899cee
--- /dev/null
+++ b/indra/llcommon/llprocess.cpp
@@ -0,0 +1,338 @@
+/** 
+ * @file llprocess.cpp
+ * @brief Utility class for launching, terminating, and tracking the state of processes.
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llprocess.h"
+#include "llsd.h"
+#include "llsdserialize.h"
+#include "stringize.h"
+
+#include <boost/foreach.hpp>
+#include <iostream>
+#include <stdexcept>
+
+/// Need an exception to avoid constructing an invalid LLProcess object, but
+/// internal use only
+struct LLProcessError: public std::runtime_error
+{
+	LLProcessError(const std::string& msg): std::runtime_error(msg) {}
+};
+
+LLProcessPtr LLProcess::create(const LLSD& params)
+{
+	try
+	{
+		return LLProcessPtr(new LLProcess(params));
+	}
+	catch (const LLProcessError& e)
+	{
+		LL_WARNS("LLProcess") << e.what() << LL_ENDL;
+		return LLProcessPtr();
+	}
+}
+
+LLProcess::LLProcess(const LLSD& params):
+	mProcessID(0),
+	mAutokill(params["autokill"].asBoolean())
+{
+	// nonstandard default bool value
+	if (! params.has("autokill"))
+		mAutokill = true;
+	if (! params.has("executable"))
+	{
+		throw LLProcessError(STRINGIZE("not launched: missing 'executable'\n"
+									   << LLSDNotationStreamer(params)));
+	}
+
+	launch(params);
+}
+
+LLProcess::~LLProcess()
+{
+	if (mAutokill)
+	{
+		kill();
+	}
+}
+
+bool LLProcess::isRunning(void)
+{
+	mProcessID = isRunning(mProcessID);
+	return (mProcessID != 0);
+}
+
+#if LL_WINDOWS
+
+static std::string quote(const std::string& str)
+{
+	std::string::size_type len(str.length());
+	// If the string is already quoted, assume user knows what s/he's doing.
+	if (len >= 2 && str[0] == '"' && str[len-1] == '"')
+	{
+		return str;
+	}
+
+	// Not already quoted: do it.
+	std::string result("\"");
+	for (std::string::const_iterator ci(str.begin()), cend(str.end()); ci != cend; ++ci)
+	{
+		if (*ci == '"')
+		{
+			result.append("\\");
+		}
+		result.push_back(*ci);
+	}
+	return result + "\"";
+}
+
+void LLProcess::launch(const LLSD& params)
+{
+	PROCESS_INFORMATION pinfo;
+	STARTUPINFOA sinfo;
+	memset(&sinfo, 0, sizeof(sinfo));
+	
+	std::string args = quote(params["executable"]);
+	BOOST_FOREACH(const std::string& arg, llsd::inArray(params["args"]))
+	{
+		args += " ";
+		args += quote(arg);
+	}
+	
+	// So retarded.  Windows requires that the second parameter to
+	// CreateProcessA be a writable (non-const) string...
+	std::vector<char> args2(args.begin(), args.end());
+	args2.push_back('\0');
+
+	// Convert wrapper to a real std::string so we can use c_str(); but use a
+	// named variable instead of a temporary so c_str() pointer remains valid.
+	std::string cwd(params["cwd"]);
+	const char * working_directory = 0;
+	if (! cwd.empty())
+		working_directory = cwd.c_str();
+	if( ! CreateProcessA( NULL, &args2[0], NULL, NULL, FALSE, 0, NULL, working_directory, &sinfo, &pinfo ) )
+	{
+		int result = GetLastError();
+
+		LPTSTR error_str = 0;
+		if(
+			FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+				NULL,
+				result,
+				0,
+				(LPTSTR)&error_str,
+				0,
+				NULL) 
+			!= 0) 
+		{
+			char message[256];
+			wcstombs(message, error_str, sizeof(message));
+			message[sizeof(message)-1] = 0;
+			LocalFree(error_str);
+			throw LLProcessError(STRINGIZE("CreateProcessA failed (" << result << "): "
+										   << message));
+		}
+		throw LLProcessError(STRINGIZE("CreateProcessA failed (" << result
+									   << "), but FormatMessage() did not explain"));
+	}
+
+	// foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
+	// CloseHandle(pinfo.hProcess); // stops leaks - nothing else
+	mProcessID = pinfo.hProcess;
+	CloseHandle(pinfo.hThread); // stops leaks - nothing else
+}
+
+LLProcess::id LLProcess::isRunning(id handle)
+{
+	if (! handle)
+		return 0;
+
+	DWORD waitresult = WaitForSingleObject(handle, 0);
+	if(waitresult == WAIT_OBJECT_0)
+	{
+		// the process has completed.
+		return 0;
+	}
+
+	return handle;
+}
+
+bool LLProcess::kill(void)
+{
+	if (! mProcessID)
+		return false;
+
+	TerminateProcess(mProcessID, 0);
+	return ! isRunning();
+}
+
+#else // Mac and linux
+
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+// 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)
+{
+	pid_t wait_result = ::waitpid(pid, NULL, WNOHANG);
+	if (wait_result == pid)
+	{
+		return true;
+	}
+	if (wait_result == -1 && errno == ECHILD)
+	{
+		// No such process -- this may mean we're ignoring SIGCHILD.
+		return true;
+	}
+	
+	return false;
+}
+
+void LLProcess::launch(const LLSD& params)
+{
+	// flush all buffers before the child inherits them
+	::fflush(NULL);
+
+	pid_t child = vfork();
+	if (child == 0)
+	{
+		// child process
+
+		std::string cwd(params["cwd"]);
+		if (! cwd.empty())
+		{
+			// change to the desired child working directory
+			if (::chdir(cwd.c_str()))
+			{
+				// chdir failed
+				LL_WARNS("LLProcess") << "could not chdir(\"" << cwd << "\")" << LL_ENDL;
+				// pointless to throw; this is child process...
+				_exit(248);
+			}
+		}
+
+		// create an argv vector for the child process
+		std::vector<const char*> fake_argv;
+
+		// add the executable path
+		std::string executable(params["executable"]);
+		fake_argv.push_back(executable.c_str());
+
+		// and any arguments
+		const LLSD& params_args(params["args"]);
+		std::vector<std::string> args(params_args.beginArray(), params_args.endArray());
+		BOOST_FOREACH(const std::string& arg, args)
+		{
+			fake_argv.push_back(arg.c_str());
+		}
+
+		// terminate with a null pointer
+		fake_argv.push_back(NULL);
+
+		::execv(executable.c_str(), const_cast<char* const*>(&fake_argv[0]));
+
+		// If we reach this point, the exec failed.
+		LL_WARNS("LLProcess") << "failed to launch: ";
+		BOOST_FOREACH(const char* arg, fake_argv)
+		{
+			LL_CONT << arg << ' ';
+		}
+		LL_CONT << LL_ENDL;
+		// Use _exit() instead of exit() per the vfork man page. Exit with a
+		// distinctive rc: someday soon we'll be able to retrieve it, and it
+		// would be nice to be able to tell that the child process failed!
+		_exit(249);
+	}
+
+	// parent process
+	mProcessID = child;
+}
+
+LLProcess::id LLProcess::isRunning(id pid)
+{
+	if (! pid)
+		return 0;
+
+	// Check whether the process has exited, and reap it if it has.
+	if(reap_pid(pid))
+	{
+		// the process has exited.
+		return 0;
+	}
+
+	return pid;
+}
+
+bool LLProcess::kill(void)
+{
+	if (! mProcessID)
+		return false;
+
+	// Try to kill the process. We'll do approximately the same thing whether
+	// the kill returns an error or not, so we ignore the result.
+	(void)::kill(mProcessID, SIGTERM);
+
+	// This will have the side-effect of reaping the zombie if the process has exited.
+	return ! isRunning();
+}
+
+/*==========================================================================*|
+static std::list<pid_t> sZombies;
+
+void LLProcess::orphan(void)
+{
+	// Disassociate the process from this object
+	if(mProcessID != 0)
+	{	
+		// We may still need to reap the process's zombie eventually
+		sZombies.push_back(mProcessID);
+	
+		mProcessID = 0;
+	}
+}
+
+// static 
+void LLProcess::reap(void)
+{
+	// Attempt to real all saved process ID's.
+	
+	std::list<pid_t>::iterator iter = sZombies.begin();
+	while(iter != sZombies.end())
+	{
+		if(reap_pid(*iter))
+		{
+			iter = sZombies.erase(iter);
+		}
+		else
+		{
+			iter++;
+		}
+	}
+}
+|*==========================================================================*/
+
+#endif
diff --git a/indra/llcommon/llprocess.h b/indra/llcommon/llprocess.h
new file mode 100644
index 0000000000000000000000000000000000000000..9a74cfe8291afae70a547aa2f305c67ee4ab5f1e
--- /dev/null
+++ b/indra/llcommon/llprocess.h
@@ -0,0 +1,106 @@
+/** 
+ * @file llprocess.h
+ * @brief Utility class for launching, terminating, and tracking child processes.
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+ 
+#ifndef LL_LLPROCESS_H
+#define LL_LLPROCESS_H
+
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+#if LL_WINDOWS
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+class LLSD;
+
+class LLProcess;
+/// LLProcess instances are created on the heap by static factory methods and
+/// managed by ref-counted pointers.
+typedef boost::shared_ptr<LLProcess> LLProcessPtr;
+
+/**
+ *	LLProcess handles launching external processes with specified command line arguments.
+ *	It also keeps track of whether the process is still running, and can kill it if required.
+*/
+class LL_COMMON_API LLProcess: public boost::noncopyable
+{
+	LOG_CLASS(LLProcess);
+public:
+
+	/**
+	 * Factory accepting LLSD::Map.
+	 * MAY RETURN DEFAULT-CONSTRUCTED LLProcessPtr if params invalid!
+	 *
+	 * executable (required, string):				executable pathname
+	 * args		  (optional, string array):			extra command-line arguments
+	 * cwd		  (optional, string, dft no chdir): change to this directory before executing
+	 * autokill	  (optional, bool, dft true):		implicit kill() on ~LLProcess
+	 */
+	static LLProcessPtr create(const LLSD& params);
+	virtual ~LLProcess();
+
+	// isRunning isn't const because, if child isn't running, it clears stored
+	// process ID
+	bool isRunning(void);
+	
+	// Attempt to kill the process -- returns true if the process is no longer running when it returns.
+	// Note that even if this returns false, the process may exit some time after it's called.
+	bool kill(void);
+
+#if LL_WINDOWS
+	typedef HANDLE id;
+#else
+	typedef pid_t  id;
+#endif	
+	/// Get platform-specific process ID
+	id getProcessID() const { return mProcessID; };
+
+	/**
+	 * Test if a process (id obtained from getProcessID()) is still
+	 * running. Return is same nonzero id value if still running, else
+	 * zero, so you can test it like a bool. But if you want to update a
+	 * stored variable as a side effect, you can write code like this:
+	 * @code
+	 * childpid = LLProcess::isRunning(childpid);
+	 * @endcode
+	 * @note This method is intended as a unit-test hook, not as the first of
+	 * a whole set of operations supported on freestanding @c id values. New
+	 * functionality should be added as nonstatic members operating on
+	 * mProcessID.
+	 */
+	static id isRunning(id);
+	
+private:
+	/// constructor is private: use create() instead
+	LLProcess(const LLSD& params);
+	void launch(const LLSD& params);
+
+	id mProcessID;
+	bool mAutokill;
+};
+
+#endif // LL_LLPROCESS_H
diff --git a/indra/llcommon/llprocesslauncher.cpp b/indra/llcommon/llprocesslauncher.cpp
deleted file mode 100644
index 5791d14ec0fd7e74801dff1a8412c32df33eb373..0000000000000000000000000000000000000000
--- a/indra/llcommon/llprocesslauncher.cpp
+++ /dev/null
@@ -1,394 +0,0 @@
-/** 
- * @file llprocesslauncher.cpp
- * @brief Utility class for launching, terminating, and tracking the state of processes.
- *
- * $LicenseInfo:firstyear=2008&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
- 
-#include "linden_common.h"
-
-#include "llprocesslauncher.h"
-
-#include <iostream>
-#if LL_DARWIN || LL_LINUX
-// not required or present on Win32
-#include <sys/wait.h>
-#endif
-
-LLProcessLauncher::LLProcessLauncher()
-{
-#if LL_WINDOWS
-	mProcessHandle = 0;
-#else
-	mProcessID = 0;
-#endif
-}
-
-LLProcessLauncher::~LLProcessLauncher()
-{
-	kill();
-}
-
-void LLProcessLauncher::setExecutable(const std::string &executable)
-{
-	mExecutable = executable;
-}
-
-void LLProcessLauncher::setWorkingDirectory(const std::string &dir)
-{
-	mWorkingDir = dir;
-}
-
-const std::string& LLProcessLauncher::getExecutable() const
-{
-	return mExecutable;
-}
-
-void LLProcessLauncher::clearArguments()
-{
-	mLaunchArguments.clear();
-}
-
-void LLProcessLauncher::addArgument(const std::string &arg)
-{
-	mLaunchArguments.push_back(arg);
-}
-
-#if LL_WINDOWS
-
-static std::string quote(const std::string& str)
-{
-    std::string::size_type len(str.length());
-    // If the string is already quoted, assume user knows what s/he's doing.
-    if (len >= 2 && str[0] == '"' && str[len-1] == '"')
-    {
-        return str;
-    }
-
-    // Not already quoted: do it.
-    std::string result("\"");
-    for (std::string::const_iterator ci(str.begin()), cend(str.end()); ci != cend; ++ci)
-    {
-        if (*ci == '"')
-        {
-            result.append("\\");
-        }
-        result.push_back(*ci);
-    }
-    return result + "\"";
-}
-
-int LLProcessLauncher::launch(void)
-{
-	// If there was already a process associated with this object, kill it.
-	kill();
-	orphan();
-
-	int result = 0;
-	
-	PROCESS_INFORMATION pinfo;
-	STARTUPINFOA sinfo;
-	memset(&sinfo, 0, sizeof(sinfo));
-	
-	std::string args = quote(mExecutable);
-	for(int i = 0; i < (int)mLaunchArguments.size(); i++)
-	{
-		args += " ";
-		args += quote(mLaunchArguments[i]);
-	}
-	
-	// So retarded.  Windows requires that the second parameter to CreateProcessA be a writable (non-const) string...
-	char *args2 = new char[args.size() + 1];
-	strcpy(args2, args.c_str());
-
-	const char * working_directory = 0;
-	if(!mWorkingDir.empty()) working_directory = mWorkingDir.c_str();
-	if( ! CreateProcessA( NULL, args2, NULL, NULL, FALSE, 0, NULL, working_directory, &sinfo, &pinfo ) )
-	{
-		result = GetLastError();
-
-		LPTSTR error_str = 0;
-		if(
-			FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
-				NULL,
-				result,
-				0,
-				(LPTSTR)&error_str,
-				0,
-				NULL) 
-			!= 0) 
-		{
-			char message[256];
-			wcstombs(message, error_str, 256);
-			message[255] = 0;
-			llwarns << "CreateProcessA failed: " << message << llendl;
-			LocalFree(error_str);
-		}
-
-		if(result == 0)
-		{
-			// Make absolutely certain we return a non-zero value on failure.
-			result = -1;
-		}
-	}
-	else
-	{
-		// foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
-		// CloseHandle(pinfo.hProcess); // stops leaks - nothing else
-		mProcessHandle = pinfo.hProcess;
-		CloseHandle(pinfo.hThread); // stops leaks - nothing else
-	}		
-	
-	delete[] args2;
-	
-	return result;
-}
-
-bool LLProcessLauncher::isRunning(void)
-{
-	mProcessHandle = isRunning(mProcessHandle);
-	return (mProcessHandle != 0);
-}
-
-LLProcessLauncher::ll_pid_t LLProcessLauncher::isRunning(ll_pid_t handle)
-{
-	if (! handle)
-		return 0;
-
-	DWORD waitresult = WaitForSingleObject(handle, 0);
-	if(waitresult == WAIT_OBJECT_0)
-	{
-		// the process has completed.
-		return 0;
-	}
-
-	return handle;
-}
-
-bool LLProcessLauncher::kill(void)
-{
-	bool result = true;
-	
-	if(mProcessHandle != 0)
-	{
-		TerminateProcess(mProcessHandle,0);
-
-		if(isRunning())
-		{
-			result = false;
-		}
-	}
-	
-	return result;
-}
-
-void LLProcessLauncher::orphan(void)
-{
-	// Forget about the process
-	mProcessHandle = 0;
-}
-
-// static 
-void LLProcessLauncher::reap(void)
-{
-	// No actions necessary on Windows.
-}
-
-#else // Mac and linux
-
-#include <signal.h>
-#include <fcntl.h>
-#include <errno.h>
-
-static std::list<pid_t> sZombies;
-
-// 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)
-{
-	bool result = false;
-	
-	pid_t wait_result = ::waitpid(pid, NULL, WNOHANG);
-	if(wait_result == pid)
-	{
-		result = true;
-	}
-	else if(wait_result == -1)
-	{
-		if(errno == ECHILD)
-		{
-			// No such process -- this may mean we're ignoring SIGCHILD.
-			result = true;
-		}
-	}
-	
-	return result;
-}
-
-int LLProcessLauncher::launch(void)
-{
-	// If there was already a process associated with this object, kill it.
-	kill();
-	orphan();
-	
-	int result = 0;
-	int current_wd = -1;
-	
-	// create an argv vector for the child process
-	const char ** fake_argv = new const char *[mLaunchArguments.size() + 2];  // 1 for the executable path, 1 for the NULL terminator
-
-	int i = 0;
-	
-	// add the executable path
-	fake_argv[i++] = mExecutable.c_str();
-	
-	// and any arguments
-	for(int j=0; j < mLaunchArguments.size(); j++)
-		fake_argv[i++] = mLaunchArguments[j].c_str();
-	
-	// terminate with a null pointer
-	fake_argv[i] = NULL;
-	
-	if(!mWorkingDir.empty())
-	{
-		// save the current working directory
-		current_wd = ::open(".", O_RDONLY);
-	
-		// and change to the one the child will be executed in
-		if (::chdir(mWorkingDir.c_str()))
-		{
-			// chdir failed
-		}
-	}
-		
- 	// flush all buffers before the child inherits them
- 	::fflush(NULL);
-
-	pid_t id = vfork();
-	if(id == 0)
-	{
-		// child process
-		::execv(mExecutable.c_str(), (char * const *)fake_argv);
-
-		// If we reach this point, the exec failed.
-        LL_WARNS("LLProcessLauncher") << "failed to launch: ";
-        for (const char * const * ai = fake_argv; *ai; ++ai)
-        {
-            LL_CONT << *ai << ' ';
-        }
-        LL_CONT << LL_ENDL;
-		// Use _exit() instead of exit() per the vfork man page. Exit with a
-		// distinctive rc: someday soon we'll be able to retrieve it, and it
-		// would be nice to be able to tell that the child process failed!
-		_exit(249);
-	}
-
-	// parent process
-	
-	if(current_wd >= 0)
-	{
-		// restore the previous working directory
-		if (::fchdir(current_wd))
-		{
-			// chdir failed
-		}
-		::close(current_wd);
-	}
-	
-	delete[] fake_argv;
-	
-	mProcessID = id;
-
-	return result;
-}
-
-bool LLProcessLauncher::isRunning(void)
-{
-	mProcessID = isRunning(mProcessID);
-	return (mProcessID != 0);
-}
-
-LLProcessLauncher::ll_pid_t LLProcessLauncher::isRunning(ll_pid_t pid)
-{
-    if (! pid)
-        return 0;
-
-    // Check whether the process has exited, and reap it if it has.
-    if(reap_pid(pid))
-    {
-        // the process has exited.
-        return 0;
-    }
-
-    return pid;
-}
-
-bool LLProcessLauncher::kill(void)
-{
-	bool result = true;
-	
-	if(mProcessID != 0)
-	{
-		// Try to kill the process.  We'll do approximately the same thing whether the kill returns an error or not, so we ignore the result.
-		(void)::kill(mProcessID, SIGTERM);
-		
-		// This will have the side-effect of reaping the zombie if the process has exited.
-		if(isRunning())
-		{
-			result = false;
-		}
-	}
-	
-	return result;
-}
-
-void LLProcessLauncher::orphan(void)
-{
-	// Disassociate the process from this object
-	if(mProcessID != 0)
-	{	
-		// We may still need to reap the process's zombie eventually
-		sZombies.push_back(mProcessID);
-	
-		mProcessID = 0;
-	}
-}
-
-// static 
-void LLProcessLauncher::reap(void)
-{
-	// Attempt to real all saved process ID's.
-	
-	std::list<pid_t>::iterator iter = sZombies.begin();
-	while(iter != sZombies.end())
-	{
-		if(reap_pid(*iter))
-		{
-			iter = sZombies.erase(iter);
-		}
-		else
-		{
-			iter++;
-		}
-	}
-}
-
-#endif
diff --git a/indra/llcommon/llprocesslauncher.h b/indra/llcommon/llprocesslauncher.h
deleted file mode 100644
index 63193abd8fb4e8f7fa39c7866385a55807c4b303..0000000000000000000000000000000000000000
--- a/indra/llcommon/llprocesslauncher.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/** 
- * @file llprocesslauncher.h
- * @brief Utility class for launching, terminating, and tracking the state of processes.
- *
- * $LicenseInfo:firstyear=2008&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
- 
-#ifndef LL_LLPROCESSLAUNCHER_H
-#define LL_LLPROCESSLAUNCHER_H
-
-#if LL_WINDOWS
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#endif
-
-
-/*
-	LLProcessLauncher handles launching external processes with specified command line arguments.
-	It also keeps track of whether the process is still running, and can kill it if required.
-*/
-
-class LL_COMMON_API LLProcessLauncher
-{
-	LOG_CLASS(LLProcessLauncher);
-public:
-	LLProcessLauncher();
-	virtual ~LLProcessLauncher();
-	
-	void setExecutable(const std::string &executable);
-	void setWorkingDirectory(const std::string &dir);
-
-	const std::string& getExecutable() const;
-
-	void clearArguments();
-	void addArgument(const std::string &arg);
-		
-	int launch(void);
-	// isRunning isn't const because, if child isn't running, it clears stored
-	// process ID
-	bool isRunning(void);
-	
-	// Attempt to kill the process -- returns true if the process is no longer running when it returns.
-	// Note that even if this returns false, the process may exit some time after it's called.
-	bool kill(void);
-	
-	// Use this if you want the external process to continue execution after the LLProcessLauncher instance controlling it is deleted.
-	// Normally, the destructor will attempt to kill the process and wait for termination.
-	// This should only be used if the viewer is about to exit -- otherwise, the child process will become a zombie after it exits.
-	void orphan(void);	
-	
-	// This needs to be called periodically on Mac/Linux to clean up zombie processes.
-	// (However, as of 2012-01-12 there are no such calls in the viewer code base. :-P )
-	static void reap(void);
-	
-	// Accessors for platform-specific process ID
-#if LL_WINDOWS
-	// (Windows flavor unused as of 2012-01-12)
-	typedef HANDLE ll_pid_t;
-	HANDLE getProcessHandle() const { return mProcessHandle; }
-	ll_pid_t getProcessID() const { return mProcessHandle; }
-#else
-	typedef pid_t ll_pid_t;
-	ll_pid_t getProcessID() const { return mProcessID; };
-#endif	
-	/**
-	 * Test if a process (ll_pid_t obtained from getProcessID()) is still
-	 * running. Return is same nonzero ll_pid_t value if still running, else
-	 * zero, so you can test it like a bool. But if you want to update a
-	 * stored variable as a side effect, you can write code like this:
-	 * @code
-	 * childpid = LLProcessLauncher::isRunning(childpid);
-	 * @endcode
-	 */
-	static ll_pid_t isRunning(ll_pid_t);
-	
-private:
-	std::string mExecutable;
-	std::string mWorkingDir;
-	std::vector<std::string> mLaunchArguments;
-	
-#if LL_WINDOWS
-	HANDLE mProcessHandle;
-#else
-	pid_t mProcessID;
-#endif
-};
-
-#endif // LL_LLPROCESSLAUNCHER_H
diff --git a/indra/llcommon/tests/llprocesslauncher_test.cpp b/indra/llcommon/tests/llprocess_test.cpp
similarity index 88%
rename from indra/llcommon/tests/llprocesslauncher_test.cpp
rename to indra/llcommon/tests/llprocess_test.cpp
index 057f83631ea8c402eb7e3e5fc4a65d36ab1fe66b..55e22abd819239d06819203d5f80d321e8818f39 100644
--- a/indra/llcommon/tests/llprocesslauncher_test.cpp
+++ b/indra/llcommon/tests/llprocess_test.cpp
@@ -1,8 +1,8 @@
 /**
- * @file   llprocesslauncher_test.cpp
+ * @file   llprocess_test.cpp
  * @author Nat Goodspeed
  * @date   2011-12-19
- * @brief  Test for llprocesslauncher.
+ * @brief  Test for llprocess.
  * 
  * $LicenseInfo:firstyear=2011&license=viewerlgpl$
  * Copyright (c) 2011, Linden Research, Inc.
@@ -12,7 +12,7 @@
 // Precompiled header
 #include "linden_common.h"
 // associated header
-#include "llprocesslauncher.h"
+#include "llprocess.h"
 // STL headers
 #include <vector>
 #include <list>
@@ -32,6 +32,7 @@
 #include "../test/manageapr.h"
 #include "../test/namedtempfile.h"
 #include "stringize.h"
+#include "llsdutil.h"
 
 #if defined(LL_WINDOWS)
 #define sleep(secs) _sleep((secs) * 1000)
@@ -88,7 +89,7 @@ static std::string readfile(const std::string& pathname, const std::string& desc
 }
 
 /**
- * Construct an LLProcessLauncher to run a Python script.
+ * Construct an LLProcess to run a Python script.
  */
 struct PythonProcessLauncher
 {
@@ -106,30 +107,30 @@ struct PythonProcessLauncher
         const char* PYTHON(getenv("PYTHON"));
         tut::ensure("Set $PYTHON to the Python interpreter", PYTHON);
 
-        mPy.setExecutable(PYTHON);
-        mPy.addArgument(mScript.getName());
+        mParams["executable"] = PYTHON;
+        mParams["args"].append(mScript.getName());
     }
 
     /// Run Python script and wait for it to complete.
     void run()
     {
-        tut::ensure_equals(STRINGIZE("Couldn't launch " << mDesc << " script"),
-                           mPy.launch(), 0);
-        // One of the irritating things about LLProcessLauncher is that
+        mPy = LLProcess::create(mParams);
+        tut::ensure(STRINGIZE("Couldn't launch " << mDesc << " script"), mPy);
+        // One of the irritating things about LLProcess is that
         // there's no API to wait for the child to terminate -- but given
         // its use in our graphics-intensive interactive viewer, it's
         // understandable.
-        while (mPy.isRunning())
+        while (mPy->isRunning())
         {
             sleep(1);
         }
     }
 
     /**
-     * Run a Python script using LLProcessLauncher, expecting that it will
+     * Run a Python script using LLProcess, expecting that it will
      * write to the file passed as its sys.argv[1]. Retrieve that output.
      *
-     * Until January 2012, LLProcessLauncher provided distressingly few
+     * Until January 2012, LLProcess provided distressingly few
      * mechanisms for a child process to communicate back to its caller --
      * not even its return code. We've introduced a convention by which we
      * create an empty temp file, pass the name of that file to our child
@@ -141,13 +142,14 @@ struct PythonProcessLauncher
     {
         NamedTempFile out("out", ""); // placeholder
         // pass name of this temporary file to the script
-        mPy.addArgument(out.getName());
+        mParams["args"].append(out.getName());
         run();
         // assuming the script wrote to that file, read it
         return readfile(out.getName(), STRINGIZE("from " << mDesc << " script"));
     }
 
-    LLProcessLauncher mPy;
+    LLSD mParams;
+    LLProcessPtr mPy;
     std::string mDesc;
     NamedTempFile mScript;
 };
@@ -203,13 +205,13 @@ class NamedTempDir: public boost::noncopyable
 *****************************************************************************/
 namespace tut
 {
-    struct llprocesslauncher_data
+    struct llprocess_data
     {
         LLAPRPool pool;
     };
-    typedef test_group<llprocesslauncher_data> llprocesslauncher_group;
-    typedef llprocesslauncher_group::object object;
-    llprocesslauncher_group llprocesslaunchergrp("llprocesslauncher");
+    typedef test_group<llprocess_data> llprocess_group;
+    typedef llprocess_group::object object;
+    llprocess_group llprocessgrp("llprocess");
 
     struct Item
     {
@@ -501,17 +503,6 @@ namespace tut
 
     template<> template<>
     void object::test<2>()
-    {
-        set_test_name("set/getExecutable()");
-        LLProcessLauncher child;
-        child.setExecutable("nonsense string");
-        ensure_equals("setExecutable() 0", child.getExecutable(), "nonsense string");
-        child.setExecutable("python");
-        ensure_equals("setExecutable() 1", child.getExecutable(), "python");
-    }
-
-    template<> template<>
-    void object::test<3>()
     {
         set_test_name("setWorkingDirectory()");
         // We want to test setWorkingDirectory(). But what directory is
@@ -524,14 +515,14 @@ namespace tut
                                  "with open(sys.argv[1], 'w') as f:\n"
                                  "    f.write(os.getcwd())\n");
         // Before running, call setWorkingDirectory()
-        py.mPy.setWorkingDirectory(tempdir.getName());
+        py.mParams["cwd"] = tempdir.getName();
         ensure_equals("os.getcwd()", py.run_read(), tempdir.getName());
     }
 
     template<> template<>
-    void object::test<4>()
+    void object::test<3>()
     {
-        set_test_name("clearArguments()");
+        set_test_name("arguments");
         PythonProcessLauncher py("args",
                                  "from __future__ import with_statement\n"
                                  "import sys\n"
@@ -539,15 +530,11 @@ namespace tut
                                  "with open(sys.argv[3], 'w') as f:\n"
                                  "    for arg in sys.argv[1:]:\n"
                                  "        print >>f, arg\n");
-        // We expect that PythonProcessLauncher has already called
-        // addArgument() with the name of its own NamedTempFile. But let's
-        // change it up.
-        py.mPy.clearArguments();
-        // re-add script pathname
-        py.mPy.addArgument(py.mScript.getName()); // sys.argv[0]
-        py.mPy.addArgument("first arg");          // sys.argv[1]
-        py.mPy.addArgument("second arg");         // sys.argv[2]
-        // run_read() calls addArgument() one more time, hence [3]
+        // We expect that PythonProcessLauncher has already appended
+        // its own NamedTempFile to mParams["args"] (sys.argv[0]).
+        py.mParams["args"].append("first arg");          // sys.argv[1]
+        py.mParams["args"].append("second arg");         // sys.argv[2]
+        // run_read() appends() one more argument, hence [3]
         std::string output(py.run_read());
         boost::split_iterator<std::string::const_iterator>
             li(output, boost::first_finder("\n")), lend;
@@ -567,7 +554,7 @@ namespace tut
     }
 
     template<> template<>
-    void object::test<5>()
+    void object::test<4>()
     {
         set_test_name("explicit kill()");
         PythonProcessLauncher py("kill()",
@@ -581,8 +568,9 @@ namespace tut
                                  "with open(sys.argv[1], 'w') as f:\n"
                                  "    f.write('bad')\n");
         NamedTempFile out("out", "not started");
-        py.mPy.addArgument(out.getName());
-        ensure_equals("couldn't launch kill() script", py.mPy.launch(), 0);
+        py.mParams["args"].append(out.getName());
+        py.mPy = LLProcess::create(py.mParams);
+        ensure("couldn't launch kill() script", py.mPy);
         // Wait for the script to wake up and do its first write
         int i = 0, timeout = 60;
         for ( ; i < timeout; ++i)
@@ -594,9 +582,9 @@ namespace tut
         // If we broke this loop because of the counter, something's wrong
         ensure("script never started", i < timeout);
         // script has performed its first write and should now be sleeping.
-        py.mPy.kill();
+        py.mPy->kill();
         // wait for the script to terminate... one way or another.
-        while (py.mPy.isRunning())
+        while (py.mPy->isRunning())
         {
             sleep(1);
         }
@@ -607,11 +595,11 @@ namespace tut
     }
 
     template<> template<>
-    void object::test<6>()
+    void object::test<5>()
     {
         set_test_name("implicit kill()");
         NamedTempFile out("out", "not started");
-        LLProcessLauncher::ll_pid_t pid(0);
+        LLProcess::id pid(0);
         {
             PythonProcessLauncher py("kill()",
                                      "from __future__ import with_statement\n"
@@ -623,10 +611,11 @@ namespace tut
                                      "# if caller hasn't managed to kill by now, bad\n"
                                      "with open(sys.argv[1], 'w') as f:\n"
                                      "    f.write('bad')\n");
-            py.mPy.addArgument(out.getName());
-            ensure_equals("couldn't launch kill() script", py.mPy.launch(), 0);
-            // Capture ll_pid_t for later
-            pid = py.mPy.getProcessID();
+            py.mParams["args"].append(out.getName());
+            py.mPy = LLProcess::create(py.mParams);
+            ensure("couldn't launch kill() script", py.mPy);
+            // Capture id for later
+            pid = py.mPy->getProcessID();
             // Wait for the script to wake up and do its first write
             int i = 0, timeout = 60;
             for ( ; i < timeout; ++i)
@@ -638,10 +627,10 @@ namespace tut
             // If we broke this loop because of the counter, something's wrong
             ensure("script never started", i < timeout);
             // Script has performed its first write and should now be sleeping.
-            // Destroy the LLProcessLauncher, which should kill the child.
+            // Destroy the LLProcess, which should kill the child.
         }
         // wait for the script to terminate... one way or another.
-        while (LLProcessLauncher::isRunning(pid))
+        while (LLProcess::isRunning(pid))
         {
             sleep(1);
         }
@@ -652,14 +641,14 @@ namespace tut
     }
 
     template<> template<>
-    void object::test<7>()
+    void object::test<6>()
     {
-        set_test_name("orphan()");
+        set_test_name("autokill");
         NamedTempFile from("from", "not started");
         NamedTempFile to("to", "");
-        LLProcessLauncher::ll_pid_t pid(0);
+        LLProcess::id pid(0);
         {
-            PythonProcessLauncher py("orphan()",
+            PythonProcessLauncher py("autokill",
                                      "from __future__ import with_statement\n"
                                      "import sys, time\n"
                                      "with open(sys.argv[1], 'w') as f:\n"
@@ -678,25 +667,24 @@ namespace tut
                                      "# okay, saw 'go', write 'ack'\n"
                                      "with open(sys.argv[1], 'w') as f:\n"
                                      "    f.write('ack')\n");
-            py.mPy.addArgument(from.getName());
-            py.mPy.addArgument(to.getName());
-            ensure_equals("couldn't launch kill() script", py.mPy.launch(), 0);
-            // Capture ll_pid_t for later
-            pid = py.mPy.getProcessID();
+            py.mParams["args"].append(from.getName());
+            py.mParams["args"].append(to.getName());
+            py.mParams["autokill"] = false;
+            py.mPy = LLProcess::create(py.mParams);
+            ensure("couldn't launch kill() script", py.mPy);
+            // Capture id for later
+            pid = py.mPy->getProcessID();
             // Wait for the script to wake up and do its first write
             int i = 0, timeout = 60;
             for ( ; i < timeout; ++i)
             {
                 sleep(1);
-                if (readfile(from.getName(), "from orphan() script") == "ok")
+                if (readfile(from.getName(), "from autokill script") == "ok")
                     break;
             }
             // If we broke this loop because of the counter, something's wrong
             ensure("script never started", i < timeout);
-            // Script has performed its first write and should now be waiting
-            // for us. Orphan it.
-            py.mPy.orphan();
-            // Now destroy the LLProcessLauncher, which should NOT kill the child!
+            // Now destroy the LLProcess, which should NOT kill the child!
         }
         // If the destructor killed the child anyway, give it time to die
         sleep(2);
@@ -707,12 +695,12 @@ namespace tut
             outf << "go";
         } // flush and close.
         // now wait for the script to terminate... one way or another.
-        while (LLProcessLauncher::isRunning(pid))
+        while (LLProcess::isRunning(pid))
         {
             sleep(1);
         }
-        // If the LLProcessLauncher destructor implicitly called kill(), the
+        // If the LLProcess destructor implicitly called kill(), the
         // script could not have written 'ack' as we expect.
-        ensure_equals("orphan() script output", readfile(from.getName()), "ack");
+        ensure_equals("autokill script output", readfile(from.getName()), "ack");
     }
 } // namespace tut
diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp
index 4359e9afb9a6256fd081230425d6507f96d69c8b..7756ba622603c66bb3c79b36a9769f65d77f459f 100644
--- a/indra/llcommon/tests/llsdserialize_test.cpp
+++ b/indra/llcommon/tests/llsdserialize_test.cpp
@@ -40,7 +40,7 @@ typedef U32 uint32_t;
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
-#include "llprocesslauncher.h"
+#include "llprocess.h"
 #endif
 
 #include "boost/range.hpp"
@@ -1557,14 +1557,15 @@ namespace tut
             }
 
 #else  // LL_DARWIN, LL_LINUX
-            LLProcessLauncher py;
-            py.setExecutable(PYTHON);
-            py.addArgument(scriptfile.getName());
-            ensure_equals(STRINGIZE("Couldn't launch " << desc << " script"), py.launch(), 0);
+            LLSD params;
+            params["executable"] = PYTHON;
+            params["args"].append(scriptfile.getName());
+            LLProcessPtr py(LLProcess::create(params));
+            ensure(STRINGIZE("Couldn't launch " << desc << " script"), py);
             // Implementing timeout would mean messing with alarm() and
             // catching SIGALRM... later maybe...
             int status(0);
-            if (waitpid(py.getProcessID(), &status, 0) == -1)
+            if (waitpid(py->getProcessID(), &status, 0) == -1)
             {
                 int waitpid_errno(errno);
                 ensure_equals(STRINGIZE("Couldn't retrieve rc from " << desc << " script: "
diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp
index 110fac0f2388abe4a39bb009cd660fe899e89e52..9b225cabb8fd3e0e68cefbb7c50859d4e7597e44 100644
--- a/indra/llplugin/llpluginprocessparent.cpp
+++ b/indra/llplugin/llpluginprocessparent.cpp
@@ -31,6 +31,7 @@
 #include "llpluginprocessparent.h"
 #include "llpluginmessagepipe.h"
 #include "llpluginmessageclasses.h"
+#include "stringize.h"
 
 #include "llapr.h"
 
@@ -134,7 +135,10 @@ LLPluginProcessParent::~LLPluginProcessParent()
 		mSharedMemoryRegions.erase(iter);
 	}
 	
-	mProcess.kill();
+	if (mProcess)
+	{
+		mProcess->kill();
+	}
 	killSockets();
 }
 
@@ -159,8 +163,8 @@ void LLPluginProcessParent::errorState(void)
 
 void LLPluginProcessParent::init(const std::string &launcher_filename, const std::string &plugin_dir, const std::string &plugin_filename, bool debug)
 {	
-	mProcess.setExecutable(launcher_filename);
-	mProcess.setWorkingDirectory(plugin_dir);
+	mProcessParams["executable"] = launcher_filename;
+	mProcessParams["cwd"] = plugin_dir;
 	mPluginFile = plugin_filename;
 	mPluginDir = plugin_dir;
 	mCPUUsage = 0.0f;
@@ -371,10 +375,8 @@ void LLPluginProcessParent::idle(void)
 				// Launch the plugin process.
 				
 				// Only argument to the launcher is the port number we're listening on
-				std::stringstream stream;
-				stream << mBoundPort;
-				mProcess.addArgument(stream.str());
-				if(mProcess.launch() != 0)
+				mProcessParams["args"].append(stringize(mBoundPort));
+				if (! (mProcess = LLProcess::create(mProcessParams)))
 				{
 					errorState();
 				}
@@ -388,19 +390,18 @@ void LLPluginProcessParent::idle(void)
 						// The command we're constructing would look like this on the command line:
 						// osascript -e 'tell application "Terminal"' -e 'set win to do script "gdb -pid 12345"' -e 'do script "continue" in win' -e 'end tell'
 
-						std::stringstream cmd;
-						
-						mDebugger.setExecutable("/usr/bin/osascript");
-						mDebugger.addArgument("-e");
-						mDebugger.addArgument("tell application \"Terminal\"");
-						mDebugger.addArgument("-e");
-						cmd << "set win to do script \"gdb -pid " << mProcess.getProcessID() << "\"";
-						mDebugger.addArgument(cmd.str());
-						mDebugger.addArgument("-e");
-						mDebugger.addArgument("do script \"continue\" in win");
-						mDebugger.addArgument("-e");
-						mDebugger.addArgument("end tell");
-						mDebugger.launch();
+						LLSD params;
+						params["executable"] = "/usr/bin/osascript";
+						params["args"].append("-e");
+						params["args"].append("tell application \"Terminal\"");
+						params["args"].append("-e");
+						params["args"].append(STRINGIZE("set win to do script \"gdb -pid "
+														<< mProcess->getProcessID() << "\""));
+						params["args"].append("-e");
+						params["args"].append("do script \"continue\" in win");
+						params["args"].append("-e");
+						params["args"].append("end tell");
+						mDebugger = LLProcess::create(params);
 
 						#endif
 					}
@@ -470,7 +471,7 @@ void LLPluginProcessParent::idle(void)
 			break;
 			
 			case STATE_EXITING:
-				if(!mProcess.isRunning())
+				if (! mProcess->isRunning())
 				{
 					setState(STATE_CLEANUP);
 				}
@@ -498,7 +499,7 @@ void LLPluginProcessParent::idle(void)
 			break;
 			
 			case STATE_CLEANUP:
-				mProcess.kill();
+				mProcess->kill();
 				killSockets();
 				setState(STATE_DONE);
 			break;
@@ -1077,7 +1078,7 @@ bool LLPluginProcessParent::pluginLockedUpOrQuit()
 {
 	bool result = false;
 	
-	if(!mProcess.isRunning())
+	if (! mProcess->isRunning())
 	{
 		LL_WARNS("Plugin") << "child exited" << LL_ENDL;
 		result = true;
diff --git a/indra/llplugin/llpluginprocessparent.h b/indra/llplugin/llpluginprocessparent.h
index c66723f1753c7a7046e21b57a3a8c2789f7ccaa1..e8bcba75e07b80f0c7b4347ad8348a84100f04c2 100644
--- a/indra/llplugin/llpluginprocessparent.h
+++ b/indra/llplugin/llpluginprocessparent.h
@@ -30,13 +30,14 @@
 #define LL_LLPLUGINPROCESSPARENT_H
 
 #include "llapr.h"
-#include "llprocesslauncher.h"
+#include "llprocess.h"
 #include "llpluginmessage.h"
 #include "llpluginmessagepipe.h"
 #include "llpluginsharedmemory.h"
 
 #include "lliosocket.h"
 #include "llthread.h"
+#include "llsd.h"
 
 class LLPluginProcessParentOwner
 {
@@ -148,8 +149,9 @@ class LLPluginProcessParent : public LLPluginMessagePipeOwner
 	LLSocket::ptr_t mListenSocket;
 	LLSocket::ptr_t mSocket;
 	U32 mBoundPort;
-	
-	LLProcessLauncher mProcess;
+
+	LLSD mProcessParams;
+	LLProcessPtr mProcess;
 	
 	std::string mPluginFile;
 	std::string mPluginDir;
@@ -171,7 +173,7 @@ class LLPluginProcessParent : public LLPluginMessagePipeOwner
 	bool mBlocked;
 	bool mPolledInput;
 
-	LLProcessLauncher mDebugger;
+	LLProcessPtr mDebugger;
 	
 	F32 mPluginLaunchTimeout;		// Somewhat longer timeout for initial launch.
 	F32 mPluginLockupTimeout;		// If we don't receive a heartbeat in this many seconds, we declare the plugin locked up.
diff --git a/indra/newview/llexternaleditor.cpp b/indra/newview/llexternaleditor.cpp
index ed1d7e860a0cfa672fe78857a6b8861059c5169f..ba58cd8067822c18b949df2db9947eb18cb83910 100644
--- a/indra/newview/llexternaleditor.cpp
+++ b/indra/newview/llexternaleditor.cpp
@@ -29,6 +29,9 @@
 
 #include "lltrans.h"
 #include "llui.h"
+#include "llprocess.h"
+#include "llsdutil.h"
+#include <boost/foreach.hpp>
 
 // static
 const std::string LLExternalEditor::sFilenameMarker = "%s";
@@ -45,19 +48,8 @@ LLExternalEditor::EErrorCode LLExternalEditor::setCommand(const std::string& env
 		return EC_NOT_SPECIFIED;
 	}
 
-	// Add the filename marker if missing.
-	if (cmd.find(sFilenameMarker) == std::string::npos)
-	{
-		cmd += " \"" + sFilenameMarker + "\"";
-		llinfos << "Adding the filename marker (" << sFilenameMarker << ")" << llendl;
-	}
-
 	string_vec_t tokens;
-	if (tokenize(tokens, cmd) < 2) // 2 = bin + at least one arg (%s)
-	{
-		llwarns << "Error parsing editor command" << llendl;
-		return EC_PARSE_ERROR;
-	}
+	tokenize(tokens, cmd);
 
 	// Check executable for existence.
 	std::string bin_path = tokens[0];
@@ -68,51 +60,60 @@ LLExternalEditor::EErrorCode LLExternalEditor::setCommand(const std::string& env
 	}
 
 	// Save command.
-	mProcess.setExecutable(bin_path);
-	mArgs.clear();
+	mProcessParams["executable"] = bin_path;
+	mProcessParams["args"].clear();
 	for (size_t i = 1; i < tokens.size(); ++i)
 	{
-		if (i > 1) mArgs += " ";
-		mArgs += "\"" + tokens[i] + "\"";
+		mProcessParams["args"].append(tokens[i]);
+	}
+
+	// Add the filename marker if missing.
+	if (cmd.find(sFilenameMarker) == std::string::npos)
+	{
+		mProcessParams["args"].append(sFilenameMarker);
+		llinfos << "Adding the filename marker (" << sFilenameMarker << ")" << llendl;
+	}
+
+	llinfos << "Setting command [" << bin_path;
+	BOOST_FOREACH(const std::string& arg, llsd::inArray(mProcessParams["args"]))
+	{
+		llcont << " \"" << arg << "\"";
 	}
-	llinfos << "Setting command [" << bin_path << " " << mArgs << "]" << llendl;
+	llcont << "]" << llendl;
 
 	return EC_SUCCESS;
 }
 
 LLExternalEditor::EErrorCode LLExternalEditor::run(const std::string& file_path)
 {
-	std::string args = mArgs;
-	if (mProcess.getExecutable().empty() || args.empty())
+	if (mProcessParams["executable"].asString().empty() || ! mProcessParams["args"].size())
 	{
 		llwarns << "Editor command not set" << llendl;
 		return EC_NOT_SPECIFIED;
 	}
 
-	// Substitute the filename marker in the command with the actual passed file name.
-	LLStringUtil::replaceString(args, sFilenameMarker, file_path);
-
-	// Split command into separate tokens.
-	string_vec_t tokens;
-	tokenize(tokens, args);
+	// Copy params block so we can replace sFilenameMarker
+	LLSD params(mProcessParams);
 
-	// Set process arguments taken from the command.
-	mProcess.clearArguments();
-	for (string_vec_t::const_iterator arg_it = tokens.begin(); arg_it != tokens.end(); ++arg_it)
+	// Substitute the filename marker in the command with the actual passed file name.
+	LLSD& args(params["args"]);
+	for (LLSD::array_iterator ai(args.beginArray()), aend(args.endArray()); ai != aend; ++ai)
 	{
-		mProcess.addArgument(*arg_it);
+		std::string sarg(*ai);
+		LLStringUtil::replaceString(sarg, sFilenameMarker, file_path);
+		*ai = sarg;
 	}
 
 	// Run the editor.
-	llinfos << "Running editor command [" << mProcess.getExecutable() + " " + args << "]" << llendl;
-	int result = mProcess.launch();
-	if (result == 0)
+	llinfos << "Running editor command [" << params["executable"];
+	BOOST_FOREACH(const std::string& arg, llsd::inArray(params["args"]))
 	{
-		// Prevent killing the process in destructor (will add it to the zombies list).
-		mProcess.orphan();
+		llcont << " \"" << arg << "\"";
 	}
-
-	return result == 0 ? EC_SUCCESS : EC_FAILED_TO_RUN;
+	llcont << "]" << llendl;
+	// Prevent killing the process in destructor.
+	params["autokill"] = false;
+	return LLProcess::create(params) ? EC_SUCCESS : EC_FAILED_TO_RUN;
 }
 
 // static
diff --git a/indra/newview/llexternaleditor.h b/indra/newview/llexternaleditor.h
index ef5db56c6ee056e6ed1ee560c20ae2e1abe51e92..e81c360c24b47dc160411a82e13998af949c1ca3 100644
--- a/indra/newview/llexternaleditor.h
+++ b/indra/newview/llexternaleditor.h
@@ -27,7 +27,7 @@
 #ifndef LL_LLEXTERNALEDITOR_H
 #define LL_LLEXTERNALEDITOR_H
 
-#include <llprocesslauncher.h>
+#include "llsd.h"
 
 /**
  * Usage:
@@ -98,8 +98,7 @@ class LLExternalEditor
 	static const std::string sSetting;
 
 
-	std::string			mArgs;
-	LLProcessLauncher	mProcess;
+	LLSD				mProcessParams;
 };
 
 #endif // LL_LLEXTERNALEDITOR_H
diff --git a/indra/viewer_components/updater/llupdateinstaller.cpp b/indra/viewer_components/updater/llupdateinstaller.cpp
index 84f23b3accb23db7c210aeb4b068f5623d132a99..e99fd0af7ee715040f6bc1737953439bb5ba2c30 100644
--- a/indra/viewer_components/updater/llupdateinstaller.cpp
+++ b/indra/viewer_components/updater/llupdateinstaller.cpp
@@ -26,10 +26,10 @@
 #include "linden_common.h"
 #include <apr_file_io.h>
 #include "llapr.h"
-#include "llprocesslauncher.h"
+#include "llprocess.h"
 #include "llupdateinstaller.h"
 #include "lldir.h" 
-
+#include "llsd.h"
 
 #if defined(LL_WINDOWS)
 #pragma warning(disable: 4702)      // disable 'unreachable code' so we can use lexical_cast (really!).
@@ -78,15 +78,13 @@ int ll_install_update(std::string const & script,
 	llinfos << "UpdateInstaller: installing " << updatePath << " using " <<
 		actualScriptPath << LL_ENDL;
 	
-	LLProcessLauncher launcher;
-	launcher.setExecutable(actualScriptPath);
-	launcher.addArgument(updatePath);
-	launcher.addArgument(ll_install_failed_marker_path());
-	launcher.addArgument(boost::lexical_cast<std::string>(required));
-	int result = launcher.launch();
-	launcher.orphan();
-	
-	return result;
+	LLSD params;
+	params["executable"] = actualScriptPath;
+	params["args"].append(updatePath);
+	params["args"].append(ll_install_failed_marker_path());
+	params["args"].append(boost::lexical_cast<std::string>(required));
+	params["autokill"] = false;
+	return LLProcess::create(params)? 0 : -1;
 }