diff options
author | Armin Weatherwax | 2010-06-14 12:04:49 +0200 |
---|---|---|
committer | Armin Weatherwax | 2010-09-23 15:38:25 +0200 |
commit | 35df5441d3e2789663532c948731aff3a1e04728 (patch) | |
tree | ac7674289784a5f96106ea507637055a8dada78a /linden/indra/llcommon | |
parent | Changed version to Experimental 2010.09.18 (diff) | |
download | meta-impy-35df5441d3e2789663532c948731aff3a1e04728.zip meta-impy-35df5441d3e2789663532c948731aff3a1e04728.tar.gz meta-impy-35df5441d3e2789663532c948731aff3a1e04728.tar.bz2 meta-impy-35df5441d3e2789663532c948731aff3a1e04728.tar.xz |
llmediaplugins first step
Diffstat (limited to 'linden/indra/llcommon')
-rw-r--r-- | linden/indra/llcommon/CMakeLists.txt | 2 | ||||
-rw-r--r-- | linden/indra/llcommon/llerrorcontrol.h | 1 | ||||
-rw-r--r-- | linden/indra/llcommon/llprocesslauncher.cpp | 346 | ||||
-rw-r--r-- | linden/indra/llcommon/llprocesslauncher.h | 86 |
4 files changed, 435 insertions, 0 deletions
diff --git a/linden/indra/llcommon/CMakeLists.txt b/linden/indra/llcommon/CMakeLists.txt index 3f14be6..1c12e55 100644 --- a/linden/indra/llcommon/CMakeLists.txt +++ b/linden/indra/llcommon/CMakeLists.txt | |||
@@ -42,6 +42,7 @@ set(llcommon_SOURCE_FILES | |||
42 | llmetrics.cpp | 42 | llmetrics.cpp |
43 | llmortician.cpp | 43 | llmortician.cpp |
44 | llprocessor.cpp | 44 | llprocessor.cpp |
45 | llprocesslauncher.cpp | ||
45 | llqueuedthread.cpp | 46 | llqueuedthread.cpp |
46 | llrand.cpp | 47 | llrand.cpp |
47 | llrun.cpp | 48 | llrun.cpp |
@@ -136,6 +137,7 @@ set(llcommon_HEADER_FILES | |||
136 | llnametable.h | 137 | llnametable.h |
137 | llpreprocessor.h | 138 | llpreprocessor.h |
138 | llpriqueuemap.h | 139 | llpriqueuemap.h |
140 | llprocesslauncher.h | ||
139 | llprocessor.h | 141 | llprocessor.h |
140 | llptrskiplist.h | 142 | llptrskiplist.h |
141 | llptrskipmap.h | 143 | llptrskipmap.h |
diff --git a/linden/indra/llcommon/llerrorcontrol.h b/linden/indra/llcommon/llerrorcontrol.h index a55d706..fae7547 100644 --- a/linden/indra/llcommon/llerrorcontrol.h +++ b/linden/indra/llcommon/llerrorcontrol.h | |||
@@ -73,6 +73,7 @@ namespace LLError | |||
73 | void setFunctionLevel(const std::string& function_name, LLError::ELevel); | 73 | void setFunctionLevel(const std::string& function_name, LLError::ELevel); |
74 | void setClassLevel(const std::string& class_name, LLError::ELevel); | 74 | void setClassLevel(const std::string& class_name, LLError::ELevel); |
75 | void setFileLevel(const std::string& file_name, LLError::ELevel); | 75 | void setFileLevel(const std::string& file_name, LLError::ELevel); |
76 | void setTagLevel(const std::string& file_name, LLError::ELevel); | ||
76 | 77 | ||
77 | void configure(const LLSD&); | 78 | void configure(const LLSD&); |
78 | // the LLSD can configure all of the settings | 79 | // the LLSD can configure all of the settings |
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 | |||
43 | LLProcessLauncher::LLProcessLauncher() | ||
44 | { | ||
45 | #if LL_WINDOWS | ||
46 | mProcessHandle = 0; | ||
47 | #else | ||
48 | mProcessID = 0; | ||
49 | #endif | ||
50 | } | ||
51 | |||
52 | LLProcessLauncher::~LLProcessLauncher() | ||
53 | { | ||
54 | kill(); | ||
55 | } | ||
56 | |||
57 | void LLProcessLauncher::setExecutable(const std::string &executable) | ||
58 | { | ||
59 | mExecutable = executable; | ||
60 | } | ||
61 | |||
62 | void LLProcessLauncher::setWorkingDirectory(const std::string &dir) | ||
63 | { | ||
64 | mWorkingDir = dir; | ||
65 | } | ||
66 | |||
67 | void LLProcessLauncher::clearArguments() | ||
68 | { | ||
69 | mLaunchArguments.clear(); | ||
70 | } | ||
71 | |||
72 | void LLProcessLauncher::addArgument(const std::string &arg) | ||
73 | { | ||
74 | mLaunchArguments.push_back(arg); | ||
75 | } | ||
76 | |||
77 | void LLProcessLauncher::addArgument(const char *arg) | ||
78 | { | ||
79 | mLaunchArguments.push_back(std::string(arg)); | ||
80 | } | ||
81 | |||
82 | #if LL_WINDOWS | ||
83 | |||
84 | int 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 | |||
131 | bool 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 | } | ||
145 | bool 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 | |||
162 | void LLProcessLauncher::orphan(void) | ||
163 | { | ||
164 | // Forget about the process | ||
165 | mProcessHandle = 0; | ||
166 | } | ||
167 | |||
168 | // static | ||
169 | void 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 | |||
180 | static 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. | ||
183 | static 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 | |||
204 | int 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 | |||
281 | bool 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 | |||
296 | bool 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 | |||
315 | void 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 | ||
328 | void 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 | ||
diff --git a/linden/indra/llcommon/llprocesslauncher.h b/linden/indra/llcommon/llprocesslauncher.h new file mode 100644 index 0000000..036732f --- /dev/null +++ b/linden/indra/llcommon/llprocesslauncher.h | |||
@@ -0,0 +1,86 @@ | |||
1 | /** | ||
2 | * @file llprocesslauncher.h | ||
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 | #ifndef LL_LLPROCESSLAUNCHER_H | ||
34 | #define LL_LLPROCESSLAUNCHER_H | ||
35 | |||
36 | #if LL_WINDOWS | ||
37 | #include <windows.h> | ||
38 | #endif | ||
39 | |||
40 | |||
41 | /* | ||
42 | LLProcessLauncher handles launching external processes with specified command line arguments. | ||
43 | It also keeps track of whether the process is still running, and can kill it if required. | ||
44 | */ | ||
45 | |||
46 | class LLProcessLauncher | ||
47 | { | ||
48 | LOG_CLASS(LLProcessLauncher); | ||
49 | public: | ||
50 | LLProcessLauncher(); | ||
51 | virtual ~LLProcessLauncher(); | ||
52 | |||
53 | void setExecutable(const std::string &executable); | ||
54 | void setWorkingDirectory(const std::string &dir); | ||
55 | |||
56 | void clearArguments(); | ||
57 | void addArgument(const std::string &arg); | ||
58 | void addArgument(const char *arg); | ||
59 | |||
60 | int launch(void); | ||
61 | bool isRunning(void); | ||
62 | |||
63 | // Attempt to kill the process -- returns true if the process is no longer running when it returns. | ||
64 | // Note that even if this returns false, the process may exit some time after it's called. | ||
65 | bool kill(void); | ||
66 | |||
67 | // Use this if you want the external process to continue execution after the LLProcessLauncher instance controlling it is deleted. | ||
68 | // Normally, the destructor will attempt to kill the process and wait for termination. | ||
69 | // This should only be used if the viewer is about to exit -- otherwise, the child process will become a zombie after it exits. | ||
70 | void orphan(void); | ||
71 | |||
72 | // This needs to be called periodically on Mac/Linux to clean up zombie processes. | ||
73 | static void reap(void); | ||
74 | private: | ||
75 | std::string mExecutable; | ||
76 | std::string mWorkingDir; | ||
77 | std::vector<std::string> mLaunchArguments; | ||
78 | |||
79 | #if LL_WINDOWS | ||
80 | HANDLE mProcessHandle; | ||
81 | #else | ||
82 | pid_t mProcessID; | ||
83 | #endif | ||
84 | }; | ||
85 | |||
86 | #endif // LL_LLPROCESSLAUNCHER_H | ||