diff options
| -rw-r--r-- | indra/llcommon/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | indra/llcommon/llprocess.cpp | 338 | ||||
| -rw-r--r-- | indra/llcommon/llprocess.h | 106 | ||||
| -rw-r--r-- | indra/llcommon/llprocesslauncher.cpp | 394 | ||||
| -rw-r--r-- | indra/llcommon/llprocesslauncher.h | 107 | ||||
| -rw-r--r-- | indra/llcommon/tests/llprocess_test.cpp (renamed from indra/llcommon/tests/llprocesslauncher_test.cpp) | 130 | ||||
| -rw-r--r-- | indra/llcommon/tests/llsdserialize_test.cpp | 13 | ||||
| -rw-r--r-- | indra/llplugin/llpluginprocessparent.cpp | 47 | ||||
| -rw-r--r-- | indra/llplugin/llpluginprocessparent.h | 10 | ||||
| -rw-r--r-- | indra/newview/llexternaleditor.cpp | 73 | ||||
| -rw-r--r-- | indra/newview/llexternaleditor.h | 5 | ||||
| -rw-r--r-- | indra/viewer_components/updater/llupdateinstaller.cpp | 20 | 
12 files changed, 591 insertions, 658 deletions
| diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 2c376bb016..e2af7265aa 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 0000000000..8c0caca680 --- /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 0000000000..9a74cfe829 --- /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 5791d14ec0..0000000000 --- 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 63193abd8f..0000000000 --- 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 index 057f83631e..55e22abd81 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 @@ private:  *****************************************************************************/  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      { @@ -502,17 +504,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          // guaranteed to exist on every machine, under every OS? Have to @@ -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 4359e9afb9..7756ba6226 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 110fac0f23..9b225cabb8 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 c66723f175..e8bcba75e0 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 @@ private:  	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 @@ private:  	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 ed1d7e860a..ba58cd8067 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 ef5db56c6e..e81c360c24 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 @@ private:  	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 84f23b3acc..e99fd0af7e 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;  } | 
