PageRenderTime 49ms CodeModel.GetById 28ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llcommon/llprocesslauncher.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 357 lines | 233 code | 64 blank | 60 comment | 41 complexity | b42c52ce55cd54cd6bf87476de6959f8 MD5 | raw file
  1/** 
  2 * @file llprocesslauncher.cpp
  3 * @brief Utility class for launching, terminating, and tracking the state of processes.
  4 *
  5 * $LicenseInfo:firstyear=2008&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26 
 27#include "linden_common.h"
 28
 29#include "llprocesslauncher.h"
 30
 31#include <iostream>
 32#if LL_DARWIN || LL_LINUX
 33// not required or present on Win32
 34#include <sys/wait.h>
 35#endif
 36
 37LLProcessLauncher::LLProcessLauncher()
 38{
 39#if LL_WINDOWS
 40	mProcessHandle = 0;
 41#else
 42	mProcessID = 0;
 43#endif
 44}
 45
 46LLProcessLauncher::~LLProcessLauncher()
 47{
 48	kill();
 49}
 50
 51void LLProcessLauncher::setExecutable(const std::string &executable)
 52{
 53	mExecutable = executable;
 54}
 55
 56void LLProcessLauncher::setWorkingDirectory(const std::string &dir)
 57{
 58	mWorkingDir = dir;
 59}
 60
 61const std::string& LLProcessLauncher::getExecutable() const
 62{
 63	return mExecutable;
 64}
 65
 66void LLProcessLauncher::clearArguments()
 67{
 68	mLaunchArguments.clear();
 69}
 70
 71void LLProcessLauncher::addArgument(const std::string &arg)
 72{
 73	mLaunchArguments.push_back(arg);
 74}
 75
 76void LLProcessLauncher::addArgument(const char *arg)
 77{
 78	mLaunchArguments.push_back(std::string(arg));
 79}
 80
 81#if LL_WINDOWS
 82
 83int LLProcessLauncher::launch(void)
 84{
 85	// If there was already a process associated with this object, kill it.
 86	kill();
 87	orphan();
 88
 89	int result = 0;
 90	
 91	PROCESS_INFORMATION pinfo;
 92	STARTUPINFOA sinfo;
 93	memset(&sinfo, 0, sizeof(sinfo));
 94	
 95	std::string args = mExecutable;
 96	for(int i = 0; i < (int)mLaunchArguments.size(); i++)
 97	{
 98		args += " ";
 99		args += mLaunchArguments[i];
100	}
101	
102	// So retarded.  Windows requires that the second parameter to CreateProcessA be a writable (non-const) string...
103	char *args2 = new char[args.size() + 1];
104	strcpy(args2, args.c_str());
105
106	const char * working_directory = 0;
107	if(!mWorkingDir.empty()) working_directory = mWorkingDir.c_str();
108	if( ! CreateProcessA( NULL, args2, NULL, NULL, FALSE, 0, NULL, working_directory, &sinfo, &pinfo ) )
109	{
110		result = GetLastError();
111
112		LPTSTR error_str = 0;
113		if(
114			FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
115				NULL,
116				result,
117				0,
118				(LPTSTR)&error_str,
119				0,
120				NULL) 
121			!= 0) 
122		{
123			char message[256];
124			wcstombs(message, error_str, 256);
125			message[255] = 0;
126			llwarns << "CreateProcessA failed: " << message << llendl;
127			LocalFree(error_str);
128		}
129
130		if(result == 0)
131		{
132			// Make absolutely certain we return a non-zero value on failure.
133			result = -1;
134		}
135	}
136	else
137	{
138		// foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
139		// CloseHandle(pinfo.hProcess); // stops leaks - nothing else
140		mProcessHandle = pinfo.hProcess;
141		CloseHandle(pinfo.hThread); // stops leaks - nothing else
142	}		
143	
144	delete[] args2;
145	
146	return result;
147}
148
149bool LLProcessLauncher::isRunning(void)
150{
151	if(mProcessHandle != 0)		
152	{
153		DWORD waitresult = WaitForSingleObject(mProcessHandle, 0);
154		if(waitresult == WAIT_OBJECT_0)
155		{
156			// the process has completed.
157			mProcessHandle = 0;
158		}			
159	}
160
161	return (mProcessHandle != 0);
162}
163bool LLProcessLauncher::kill(void)
164{
165	bool result = true;
166	
167	if(mProcessHandle != 0)
168	{
169		TerminateProcess(mProcessHandle,0);
170
171		if(isRunning())
172		{
173			result = false;
174		}
175	}
176	
177	return result;
178}
179
180void LLProcessLauncher::orphan(void)
181{
182	// Forget about the process
183	mProcessHandle = 0;
184}
185
186// static 
187void LLProcessLauncher::reap(void)
188{
189	// No actions necessary on Windows.
190}
191
192#else // Mac and linux
193
194#include <signal.h>
195#include <fcntl.h>
196#include <errno.h>
197
198static std::list<pid_t> sZombies;
199
200// Attempt to reap a process ID -- returns true if the process has exited and been reaped, false otherwise.
201static bool reap_pid(pid_t pid)
202{
203	bool result = false;
204	
205	pid_t wait_result = ::waitpid(pid, NULL, WNOHANG);
206	if(wait_result == pid)
207	{
208		result = true;
209	}
210	else if(wait_result == -1)
211	{
212		if(errno == ECHILD)
213		{
214			// No such process -- this may mean we're ignoring SIGCHILD.
215			result = true;
216		}
217	}
218	
219	return result;
220}
221
222int LLProcessLauncher::launch(void)
223{
224	// If there was already a process associated with this object, kill it.
225	kill();
226	orphan();
227	
228	int result = 0;
229	int current_wd = -1;
230	
231	// create an argv vector for the child process
232	const char ** fake_argv = new const char *[mLaunchArguments.size() + 2];  // 1 for the executable path, 1 for the NULL terminator
233
234	int i = 0;
235	
236	// add the executable path
237	fake_argv[i++] = mExecutable.c_str();
238	
239	// and any arguments
240	for(int j=0; j < mLaunchArguments.size(); j++)
241		fake_argv[i++] = mLaunchArguments[j].c_str();
242	
243	// terminate with a null pointer
244	fake_argv[i] = NULL;
245	
246	if(!mWorkingDir.empty())
247	{
248		// save the current working directory
249		current_wd = ::open(".", O_RDONLY);
250	
251		// and change to the one the child will be executed in
252		if (::chdir(mWorkingDir.c_str()))
253		{
254			// chdir failed
255		}
256	}
257		
258 	// flush all buffers before the child inherits them
259 	::fflush(NULL);
260
261	pid_t id = vfork();
262	if(id == 0)
263	{
264		// child process
265		
266		::execv(mExecutable.c_str(), (char * const *)fake_argv);
267		
268		// If we reach this point, the exec failed.
269		// Use _exit() instead of exit() per the vfork man page.
270		_exit(0);
271	}
272
273	// parent process
274	
275	if(current_wd >= 0)
276	{
277		// restore the previous working directory
278		if (::fchdir(current_wd))
279		{
280			// chdir failed
281		}
282		::close(current_wd);
283	}
284	
285	delete[] fake_argv;
286	
287	mProcessID = id;
288
289	return result;
290}
291
292bool LLProcessLauncher::isRunning(void)
293{
294	if(mProcessID != 0)
295	{
296		// Check whether the process has exited, and reap it if it has.
297		if(reap_pid(mProcessID))
298		{
299			// the process has exited.
300			mProcessID = 0;
301		}
302	}
303	
304	return (mProcessID != 0);
305}
306
307bool LLProcessLauncher::kill(void)
308{
309	bool result = true;
310	
311	if(mProcessID != 0)
312	{
313		// 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.
314		(void)::kill(mProcessID, SIGTERM);
315		
316		// This will have the side-effect of reaping the zombie if the process has exited.
317		if(isRunning())
318		{
319			result = false;
320		}
321	}
322	
323	return result;
324}
325
326void LLProcessLauncher::orphan(void)
327{
328	// Disassociate the process from this object
329	if(mProcessID != 0)
330	{	
331		// We may still need to reap the process's zombie eventually
332		sZombies.push_back(mProcessID);
333	
334		mProcessID = 0;
335	}
336}
337
338// static 
339void LLProcessLauncher::reap(void)
340{
341	// Attempt to real all saved process ID's.
342	
343	std::list<pid_t>::iterator iter = sZombies.begin();
344	while(iter != sZombies.end())
345	{
346		if(reap_pid(*iter))
347		{
348			iter = sZombies.erase(iter);
349		}
350		else
351		{
352			iter++;
353		}
354	}
355}
356
357#endif