aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llcommon/llprocesslauncher.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/llcommon/llprocesslauncher.cpp')
-rw-r--r--linden/indra/llcommon/llprocesslauncher.cpp346
1 files changed, 346 insertions, 0 deletions
diff --git a/linden/indra/llcommon/llprocesslauncher.cpp b/linden/indra/llcommon/llprocesslauncher.cpp
new file mode 100644
index 0000000..e27aaa3
--- /dev/null
+++ b/linden/indra/llcommon/llprocesslauncher.cpp
@@ -0,0 +1,346 @@
1/**
2 * @file llprocesslauncher.cpp
3 * @brief Utility class for launching, terminating, and tracking the state of processes.
4 *
5 * $LicenseInfo:firstyear=2008&license=viewergpl$
6 *
7 * Copyright (c) 2008-2009, Linden Research, Inc.
8 *
9 * Second Life Viewer Source Code
10 * The source code in this file ("Source Code") is provided by Linden Lab
11 * to you under the terms of the GNU General Public License, version 2.0
12 * ("GPL"), unless you have obtained a separate licensing agreement
13 * ("Other License"), formally executed by you and Linden Lab. Terms of
14 * the GPL can be found in doc/GPL-license.txt in this distribution, or
15 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
16 *
17 * There are special exceptions to the terms and conditions of the GPL as
18 * it is applied to this Source Code. View the full text of the exception
19 * in the file doc/FLOSS-exception.txt in this software distribution, or
20 * online at
21 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
22 *
23 * By copying, modifying or distributing this software, you acknowledge
24 * that you have read and understood your obligations described above,
25 * and agree to abide by those obligations.
26 *
27 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
28 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
29 * COMPLETENESS OR PERFORMANCE.
30 * $/LicenseInfo$
31 */
32
33#include "linden_common.h"
34
35#include "llprocesslauncher.h"
36
37#include <iostream>
38#if LL_DARWIN || LL_LINUX
39// not required or present on Win32
40#include <sys/wait.h>
41#endif
42
43LLProcessLauncher::LLProcessLauncher()
44{
45#if LL_WINDOWS
46 mProcessHandle = 0;
47#else
48 mProcessID = 0;
49#endif
50}
51
52LLProcessLauncher::~LLProcessLauncher()
53{
54 kill();
55}
56
57void LLProcessLauncher::setExecutable(const std::string &executable)
58{
59 mExecutable = executable;
60}
61
62void LLProcessLauncher::setWorkingDirectory(const std::string &dir)
63{
64 mWorkingDir = dir;
65}
66
67void LLProcessLauncher::clearArguments()
68{
69 mLaunchArguments.clear();
70}
71
72void LLProcessLauncher::addArgument(const std::string &arg)
73{
74 mLaunchArguments.push_back(arg);
75}
76
77void LLProcessLauncher::addArgument(const char *arg)
78{
79 mLaunchArguments.push_back(std::string(arg));
80}
81
82#if LL_WINDOWS
83
84int LLProcessLauncher::launch(void)
85{
86 // If there was already a process associated with this object, kill it.
87 kill();
88 orphan();
89
90 int result = 0;
91
92 PROCESS_INFORMATION pinfo;
93 STARTUPINFOA sinfo;
94 memset(&sinfo, 0, sizeof(sinfo));
95
96 std::string args = "\"" + mExecutable + "\"";
97 for(int i = 0; i < (int)mLaunchArguments.size(); i++)
98 {
99 args += " ";
100 args += mLaunchArguments[i];
101 }
102 LL_INFOS("Plugin") << "Executable: " << mExecutable << " arguments: " << args << LL_ENDL;
103
104 // So retarded. Windows requires that the second parameter to CreateProcessA be a writable (non-const) string...
105 char *args2 = new char[args.size() + 1];
106 strcpy(args2, args.c_str());
107
108 if( ! CreateProcessA( NULL, args2, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo ) )
109 {
110 // TODO: do better than returning the OS-specific error code on failure...
111 result = GetLastError();
112 if(result == 0)
113 {
114 // Make absolutely certain we return a non-zero value on failure.
115 result = -1;
116 }
117 }
118 else
119 {
120 // foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
121 // CloseHandle(pinfo.hProcess); // stops leaks - nothing else
122 mProcessHandle = pinfo.hProcess;
123 CloseHandle(pinfo.hThread); // stops leaks - nothing else
124 }
125
126 delete[] args2;
127
128 return result;
129}
130
131bool LLProcessLauncher::isRunning(void)
132{
133 if(mProcessHandle != 0)
134 {
135 DWORD waitresult = WaitForSingleObject(mProcessHandle, 0);
136 if(waitresult == WAIT_OBJECT_0)
137 {
138 // the process has completed.
139 mProcessHandle = 0;
140 }
141 }
142
143 return (mProcessHandle != 0);
144}
145bool LLProcessLauncher::kill(void)
146{
147 bool result = true;
148
149 if(mProcessHandle != 0)
150 {
151 TerminateProcess(mProcessHandle,0);
152
153 if(isRunning())
154 {
155 result = false;
156 }
157 }
158
159 return result;
160}
161
162void LLProcessLauncher::orphan(void)
163{
164 // Forget about the process
165 mProcessHandle = 0;
166}
167
168// static
169void LLProcessLauncher::reap(void)
170{
171 // No actions necessary on Windows.
172}
173
174#else // Mac and linux
175
176#include <signal.h>
177#include <fcntl.h>
178#include <errno.h>
179
180static std::list<pid_t> sZombies;
181
182// Attempt to reap a process ID -- returns true if the process has exited and been reaped, false otherwise.
183static bool reap_pid(pid_t pid)
184{
185 bool result = false;
186
187 pid_t wait_result = ::waitpid(pid, NULL, WNOHANG);
188 if(wait_result == pid)
189 {
190 result = true;
191 }
192 else if(wait_result == -1)
193 {
194 if(errno == ECHILD)
195 {
196 // No such process -- this may mean we're ignoring SIGCHILD.
197 result = true;
198 }
199 }
200
201 return result;
202}
203
204int LLProcessLauncher::launch(void)
205{
206 // If there was already a process associated with this object, kill it.
207 kill();
208 orphan();
209
210 int result = 0;
211 int current_wd = -1;
212
213 // create an argv vector for the child process
214 const char ** fake_argv = new const char *[mLaunchArguments.size() + 2]; // 1 for the executable path, 1 for the NULL terminator
215
216 int i = 0;
217
218 // add the executable path
219 fake_argv[i++] = mExecutable.c_str();
220
221 // and any arguments
222 for(int j=0; j < mLaunchArguments.size(); j++)
223 fake_argv[i++] = mLaunchArguments[j].c_str();
224
225 // terminate with a null pointer
226 fake_argv[i] = NULL;
227
228 if(!mWorkingDir.empty())
229 {
230 // save the current working directory
231 current_wd = ::open(".", O_RDONLY);
232
233 // and change to the one the child will be executed in
234 if (::chdir(mWorkingDir.c_str()))
235 {
236 // chdir failed
237 }
238 }
239
240 // flush all buffers before the child inherits them
241 ::fflush(NULL);
242
243 pid_t id = vfork();
244 if(id == 0)
245 {
246 // child process
247
248 ::execv(mExecutable.c_str(), (char * const *)fake_argv);
249
250 // If we reach this point, the exec failed.
251 // Use _exit() instead of exit() per the vfork man page.
252 _exit(0);
253 }
254
255 // parent process
256
257 if(current_wd >= 0)
258 {
259 // restore the previous working directory
260 if (::fchdir(current_wd))
261 {
262 // chdir failed
263 }
264 ::close(current_wd);
265 }
266
267 delete[] fake_argv;
268
269 mProcessID = id;
270
271 // At this point, the child process will have been created (since that's how vfork works -- the child borrowed our execution context until it forked)
272 // If the process doesn't exist at this point, the exec failed.
273 if(!isRunning())
274 {
275 result = -1;
276 }
277
278 return result;
279}
280
281bool LLProcessLauncher::isRunning(void)
282{
283 if(mProcessID != 0)
284 {
285 // Check whether the process has exited, and reap it if it has.
286 if(reap_pid(mProcessID))
287 {
288 // the process has exited.
289 mProcessID = 0;
290 }
291 }
292
293 return (mProcessID != 0);
294}
295
296bool LLProcessLauncher::kill(void)
297{
298 bool result = true;
299
300 if(mProcessID != 0)
301 {
302 // 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.
303 (void)::kill(mProcessID, SIGTERM);
304
305 // This will have the side-effect of reaping the zombie if the process has exited.
306 if(isRunning())
307 {
308 result = false;
309 }
310 }
311
312 return result;
313}
314
315void LLProcessLauncher::orphan(void)
316{
317 // Disassociate the process from this object
318 if(mProcessID != 0)
319 {
320 // We may still need to reap the process's zombie eventually
321 sZombies.push_back(mProcessID);
322
323 mProcessID = 0;
324 }
325}
326
327// static
328void LLProcessLauncher::reap(void)
329{
330 // Attempt to real all saved process ID's.
331
332 std::list<pid_t>::iterator iter = sZombies.begin();
333 while(iter != sZombies.end())
334 {
335 if(reap_pid(*iter))
336 {
337 iter = sZombies.erase(iter);
338 }
339 else
340 {
341 iter++;
342 }
343 }
344}
345
346#endif