/src/mozilla-central/xpcom/threads/nsProcessCommon.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | /***************************************************************************** |
8 | | * |
9 | | * nsProcess is used to execute new processes and specify if you want to |
10 | | * wait (blocking) or continue (non-blocking). |
11 | | * |
12 | | ***************************************************************************** |
13 | | */ |
14 | | |
15 | | #include "mozilla/ArrayUtils.h" |
16 | | |
17 | | #include "nsCOMPtr.h" |
18 | | #include "nsAutoPtr.h" |
19 | | #include "nsMemory.h" |
20 | | #include "nsProcess.h" |
21 | | #include "prio.h" |
22 | | #include "prenv.h" |
23 | | #include "nsCRT.h" |
24 | | #include "nsThreadUtils.h" |
25 | | #include "nsIObserverService.h" |
26 | | #include "nsXULAppAPI.h" |
27 | | #include "mozilla/Services.h" |
28 | | #include "GeckoProfiler.h" |
29 | | |
30 | | #include <stdlib.h> |
31 | | |
32 | | #if defined(PROCESSMODEL_WINAPI) |
33 | | #include "nsString.h" |
34 | | #include "nsLiteralString.h" |
35 | | #include "nsReadableUtils.h" |
36 | | #include "mozilla/UniquePtrExtensions.h" |
37 | | #else |
38 | | #ifdef XP_MACOSX |
39 | | #include <crt_externs.h> |
40 | | #include <spawn.h> |
41 | | #endif |
42 | | #ifdef XP_UNIX |
43 | | #ifndef XP_MACOSX |
44 | | #include "base/process_util.h" |
45 | | #endif |
46 | | #include <sys/wait.h> |
47 | | #include <sys/errno.h> |
48 | | #endif |
49 | | #include <sys/types.h> |
50 | | #include <signal.h> |
51 | | #endif |
52 | | |
53 | | using namespace mozilla; |
54 | | |
55 | | //-------------------------------------------------------------------// |
56 | | // nsIProcess implementation |
57 | | //-------------------------------------------------------------------// |
58 | | NS_IMPL_ISUPPORTS(nsProcess, nsIProcess, |
59 | | nsIObserver) |
60 | | |
61 | | //Constructor |
62 | | nsProcess::nsProcess() |
63 | | : mThread(nullptr) |
64 | | , mLock("nsProcess.mLock") |
65 | | , mShutdown(false) |
66 | | , mBlocking(false) |
67 | | , mStartHidden(false) |
68 | | , mNoShell(false) |
69 | | , mPid(-1) |
70 | | , mObserver(nullptr) |
71 | | , mWeakObserver(nullptr) |
72 | | , mExitValue(-1) |
73 | | #if !defined(XP_UNIX) |
74 | | , mProcess(nullptr) |
75 | | #endif |
76 | 0 | { |
77 | 0 | } |
78 | | |
79 | | //Destructor |
80 | | nsProcess::~nsProcess() |
81 | 0 | { |
82 | 0 | } |
83 | | |
84 | | NS_IMETHODIMP |
85 | | nsProcess::Init(nsIFile* aExecutable) |
86 | 0 | { |
87 | 0 | if (mExecutable) { |
88 | 0 | return NS_ERROR_ALREADY_INITIALIZED; |
89 | 0 | } |
90 | 0 | |
91 | 0 | if (NS_WARN_IF(!aExecutable)) { |
92 | 0 | return NS_ERROR_INVALID_ARG; |
93 | 0 | } |
94 | 0 | bool isFile; |
95 | 0 |
|
96 | 0 | //First make sure the file exists |
97 | 0 | nsresult rv = aExecutable->IsFile(&isFile); |
98 | 0 | if (NS_FAILED(rv)) { |
99 | 0 | return rv; |
100 | 0 | } |
101 | 0 | if (!isFile) { |
102 | 0 | return NS_ERROR_FAILURE; |
103 | 0 | } |
104 | 0 | |
105 | 0 | //Store the nsIFile in mExecutable |
106 | 0 | mExecutable = aExecutable; |
107 | 0 | //Get the path because it is needed by the NSPR process creation |
108 | | #ifdef XP_WIN |
109 | | rv = mExecutable->GetTarget(mTargetPath); |
110 | | if (NS_FAILED(rv) || mTargetPath.IsEmpty()) |
111 | | #endif |
112 | | rv = mExecutable->GetPath(mTargetPath); |
113 | 0 |
|
114 | 0 | return rv; |
115 | 0 | } |
116 | | |
117 | | |
118 | | #if defined(XP_WIN) |
119 | | // Out param `aWideCmdLine` must be free()d by the caller. |
120 | | static int |
121 | | assembleCmdLine(char* const* aArgv, wchar_t** aWideCmdLine, UINT aCodePage) |
122 | | { |
123 | | char* const* arg; |
124 | | char* p; |
125 | | char* q; |
126 | | char* cmdLine; |
127 | | int cmdLineSize; |
128 | | int numBackslashes; |
129 | | int i; |
130 | | int argNeedQuotes; |
131 | | |
132 | | /* |
133 | | * Find out how large the command line buffer should be. |
134 | | */ |
135 | | cmdLineSize = 0; |
136 | | for (arg = aArgv; *arg; ++arg) { |
137 | | /* |
138 | | * \ and " need to be escaped by a \. In the worst case, |
139 | | * every character is a \ or ", so the string of length |
140 | | * may double. If we quote an argument, that needs two ". |
141 | | * Finally, we need a space between arguments, and |
142 | | * a null byte at the end of command line. |
143 | | */ |
144 | | cmdLineSize += 2 * strlen(*arg) /* \ and " need to be escaped */ |
145 | | + 2 /* we quote every argument */ |
146 | | + 1; /* space in between, or final null */ |
147 | | } |
148 | | p = cmdLine = (char*) malloc(cmdLineSize * sizeof(char)); |
149 | | if (!p) { |
150 | | return -1; |
151 | | } |
152 | | |
153 | | for (arg = aArgv; *arg; ++arg) { |
154 | | /* Add a space to separates the arguments */ |
155 | | if (arg != aArgv) { |
156 | | *p++ = ' '; |
157 | | } |
158 | | q = *arg; |
159 | | numBackslashes = 0; |
160 | | argNeedQuotes = 0; |
161 | | |
162 | | /* If the argument contains white space, it needs to be quoted. */ |
163 | | if (strpbrk(*arg, " \f\n\r\t\v")) { |
164 | | argNeedQuotes = 1; |
165 | | } |
166 | | |
167 | | if (argNeedQuotes) { |
168 | | *p++ = '"'; |
169 | | } |
170 | | while (*q) { |
171 | | if (*q == '\\') { |
172 | | numBackslashes++; |
173 | | q++; |
174 | | } else if (*q == '"') { |
175 | | if (numBackslashes) { |
176 | | /* |
177 | | * Double the backslashes since they are followed |
178 | | * by a quote |
179 | | */ |
180 | | for (i = 0; i < 2 * numBackslashes; i++) { |
181 | | *p++ = '\\'; |
182 | | } |
183 | | numBackslashes = 0; |
184 | | } |
185 | | /* To escape the quote */ |
186 | | *p++ = '\\'; |
187 | | *p++ = *q++; |
188 | | } else { |
189 | | if (numBackslashes) { |
190 | | /* |
191 | | * Backslashes are not followed by a quote, so |
192 | | * don't need to double the backslashes. |
193 | | */ |
194 | | for (i = 0; i < numBackslashes; i++) { |
195 | | *p++ = '\\'; |
196 | | } |
197 | | numBackslashes = 0; |
198 | | } |
199 | | *p++ = *q++; |
200 | | } |
201 | | } |
202 | | |
203 | | /* Now we are at the end of this argument */ |
204 | | if (numBackslashes) { |
205 | | /* |
206 | | * Double the backslashes if we have a quote string |
207 | | * delimiter at the end. |
208 | | */ |
209 | | if (argNeedQuotes) { |
210 | | numBackslashes *= 2; |
211 | | } |
212 | | for (i = 0; i < numBackslashes; i++) { |
213 | | *p++ = '\\'; |
214 | | } |
215 | | } |
216 | | if (argNeedQuotes) { |
217 | | *p++ = '"'; |
218 | | } |
219 | | } |
220 | | |
221 | | *p = '\0'; |
222 | | int32_t numChars = MultiByteToWideChar(aCodePage, 0, cmdLine, -1, nullptr, 0); |
223 | | *aWideCmdLine = (wchar_t*) malloc(numChars * sizeof(wchar_t)); |
224 | | MultiByteToWideChar(aCodePage, 0, cmdLine, -1, *aWideCmdLine, numChars); |
225 | | free(cmdLine); |
226 | | return 0; |
227 | | } |
228 | | #endif |
229 | | |
230 | | void |
231 | | nsProcess::Monitor(void* aArg) |
232 | 0 | { |
233 | 0 | RefPtr<nsProcess> process = dont_AddRef(static_cast<nsProcess*>(aArg)); |
234 | 0 |
|
235 | 0 | #ifdef MOZ_GECKO_PROFILER |
236 | 0 | Maybe<AutoProfilerRegisterThread> registerThread; |
237 | 0 | if (!process->mBlocking) { |
238 | 0 | registerThread.emplace("RunProcess"); |
239 | 0 | } |
240 | 0 | #endif |
241 | 0 | if (!process->mBlocking) { |
242 | 0 | NS_SetCurrentThreadName("RunProcess"); |
243 | 0 | } |
244 | 0 |
|
245 | | #if defined(PROCESSMODEL_WINAPI) |
246 | | DWORD dwRetVal; |
247 | | unsigned long exitCode = -1; |
248 | | |
249 | | dwRetVal = WaitForSingleObject(process->mProcess, INFINITE); |
250 | | if (dwRetVal != WAIT_FAILED) { |
251 | | if (GetExitCodeProcess(process->mProcess, &exitCode) == FALSE) { |
252 | | exitCode = -1; |
253 | | } |
254 | | } |
255 | | |
256 | | // Lock in case Kill or GetExitCode are called during this |
257 | | { |
258 | | MutexAutoLock lock(process->mLock); |
259 | | CloseHandle(process->mProcess); |
260 | | process->mProcess = nullptr; |
261 | | process->mExitValue = exitCode; |
262 | | if (process->mShutdown) { |
263 | | return; |
264 | | } |
265 | | } |
266 | | #else |
267 | | #ifdef XP_UNIX |
268 | 0 | int exitCode = -1; |
269 | 0 | int status = 0; |
270 | 0 | pid_t result; |
271 | 0 | do { |
272 | 0 | result = waitpid(process->mPid, &status, 0); |
273 | 0 | } while (result == -1 && errno == EINTR); |
274 | 0 | if (result == process->mPid) { |
275 | 0 | if (WIFEXITED(status)) { |
276 | 0 | exitCode = WEXITSTATUS(status); |
277 | 0 | } else if (WIFSIGNALED(status)) { |
278 | 0 | exitCode = 256; // match NSPR's signal exit status |
279 | 0 | } |
280 | 0 | } |
281 | | #else |
282 | | int32_t exitCode = -1; |
283 | | if (PR_WaitProcess(process->mProcess, &exitCode) != PR_SUCCESS) { |
284 | | exitCode = -1; |
285 | | } |
286 | | #endif |
287 | |
|
288 | 0 | // Lock in case Kill or GetExitCode are called during this |
289 | 0 | { |
290 | 0 | MutexAutoLock lock(process->mLock); |
291 | | #if !defined(XP_UNIX) |
292 | | process->mProcess = nullptr; |
293 | | #endif |
294 | | process->mExitValue = exitCode; |
295 | 0 | if (process->mShutdown) { |
296 | 0 | return; |
297 | 0 | } |
298 | 0 | } |
299 | 0 | #endif |
300 | 0 | |
301 | 0 | // If we ran a background thread for the monitor then notify on the main |
302 | 0 | // thread |
303 | 0 | if (NS_IsMainThread()) { |
304 | 0 | process->ProcessComplete(); |
305 | 0 | } else { |
306 | 0 | NS_DispatchToMainThread(NewRunnableMethod( |
307 | 0 | "nsProcess::ProcessComplete", process, &nsProcess::ProcessComplete)); |
308 | 0 | } |
309 | 0 | } |
310 | | |
311 | | void |
312 | | nsProcess::ProcessComplete() |
313 | 0 | { |
314 | 0 | if (mThread) { |
315 | 0 | nsCOMPtr<nsIObserverService> os = |
316 | 0 | mozilla::services::GetObserverService(); |
317 | 0 | if (os) { |
318 | 0 | os->RemoveObserver(this, "xpcom-shutdown"); |
319 | 0 | } |
320 | 0 | PR_JoinThread(mThread); |
321 | 0 | mThread = nullptr; |
322 | 0 | } |
323 | 0 |
|
324 | 0 | const char* topic; |
325 | 0 | if (mExitValue < 0) { |
326 | 0 | topic = "process-failed"; |
327 | 0 | } else { |
328 | 0 | topic = "process-finished"; |
329 | 0 | } |
330 | 0 |
|
331 | 0 | mPid = -1; |
332 | 0 | nsCOMPtr<nsIObserver> observer; |
333 | 0 | if (mWeakObserver) { |
334 | 0 | observer = do_QueryReferent(mWeakObserver); |
335 | 0 | } else if (mObserver) { |
336 | 0 | observer = mObserver; |
337 | 0 | } |
338 | 0 | mObserver = nullptr; |
339 | 0 | mWeakObserver = nullptr; |
340 | 0 |
|
341 | 0 | if (observer) { |
342 | 0 | observer->Observe(NS_ISUPPORTS_CAST(nsIProcess*, this), topic, nullptr); |
343 | 0 | } |
344 | 0 | } |
345 | | |
346 | | // XXXldb |aArgs| has the wrong const-ness |
347 | | NS_IMETHODIMP |
348 | | nsProcess::Run(bool aBlocking, const char** aArgs, uint32_t aCount) |
349 | 0 | { |
350 | 0 | return CopyArgsAndRunProcess(aBlocking, aArgs, aCount, nullptr, false); |
351 | 0 | } |
352 | | |
353 | | // XXXldb |aArgs| has the wrong const-ness |
354 | | NS_IMETHODIMP |
355 | | nsProcess::RunAsync(const char** aArgs, uint32_t aCount, |
356 | | nsIObserver* aObserver, bool aHoldWeak) |
357 | 0 | { |
358 | 0 | return CopyArgsAndRunProcess(false, aArgs, aCount, aObserver, aHoldWeak); |
359 | 0 | } |
360 | | |
361 | | nsresult |
362 | | nsProcess::CopyArgsAndRunProcess(bool aBlocking, const char** aArgs, |
363 | | uint32_t aCount, nsIObserver* aObserver, |
364 | | bool aHoldWeak) |
365 | 0 | { |
366 | 0 | // Add one to the aCount for the program name and one for null termination. |
367 | 0 | char** my_argv = nullptr; |
368 | 0 | my_argv = (char**)moz_xmalloc(sizeof(char*) * (aCount + 2)); |
369 | 0 |
|
370 | 0 | my_argv[0] = ToNewUTF8String(mTargetPath); |
371 | 0 |
|
372 | 0 | for (uint32_t i = 0; i < aCount; ++i) { |
373 | 0 | my_argv[i + 1] = const_cast<char*>(aArgs[i]); |
374 | 0 | } |
375 | 0 |
|
376 | 0 | my_argv[aCount + 1] = nullptr; |
377 | 0 |
|
378 | 0 | nsresult rv = RunProcess(aBlocking, my_argv, aObserver, aHoldWeak, false); |
379 | 0 |
|
380 | 0 | free(my_argv[0]); |
381 | 0 | free(my_argv); |
382 | 0 | return rv; |
383 | 0 | } |
384 | | |
385 | | // XXXldb |aArgs| has the wrong const-ness |
386 | | NS_IMETHODIMP |
387 | | nsProcess::Runw(bool aBlocking, const char16_t** aArgs, uint32_t aCount) |
388 | 0 | { |
389 | 0 | return CopyArgsAndRunProcessw(aBlocking, aArgs, aCount, nullptr, false); |
390 | 0 | } |
391 | | |
392 | | // XXXldb |aArgs| has the wrong const-ness |
393 | | NS_IMETHODIMP |
394 | | nsProcess::RunwAsync(const char16_t** aArgs, uint32_t aCount, |
395 | | nsIObserver* aObserver, bool aHoldWeak) |
396 | 0 | { |
397 | 0 | return CopyArgsAndRunProcessw(false, aArgs, aCount, aObserver, aHoldWeak); |
398 | 0 | } |
399 | | |
400 | | nsresult |
401 | | nsProcess::CopyArgsAndRunProcessw(bool aBlocking, const char16_t** aArgs, |
402 | | uint32_t aCount, nsIObserver* aObserver, |
403 | | bool aHoldWeak) |
404 | 0 | { |
405 | 0 | // Add one to the aCount for the program name and one for null termination. |
406 | 0 | char** my_argv = nullptr; |
407 | 0 | my_argv = (char**)moz_xmalloc(sizeof(char*) * (aCount + 2)); |
408 | 0 |
|
409 | 0 | my_argv[0] = ToNewUTF8String(mTargetPath); |
410 | 0 |
|
411 | 0 | for (uint32_t i = 0; i < aCount; i++) { |
412 | 0 | my_argv[i + 1] = ToNewUTF8String(nsDependentString(aArgs[i])); |
413 | 0 | } |
414 | 0 |
|
415 | 0 | my_argv[aCount + 1] = nullptr; |
416 | 0 |
|
417 | 0 | nsresult rv = RunProcess(aBlocking, my_argv, aObserver, aHoldWeak, true); |
418 | 0 |
|
419 | 0 | for (uint32_t i = 0; i <= aCount; ++i) { |
420 | 0 | free(my_argv[i]); |
421 | 0 | } |
422 | 0 | free(my_argv); |
423 | 0 | return rv; |
424 | 0 | } |
425 | | |
426 | | nsresult |
427 | | nsProcess::RunProcess(bool aBlocking, char** aMyArgv, nsIObserver* aObserver, |
428 | | bool aHoldWeak, bool aArgsUTF8) |
429 | 0 | { |
430 | 0 | NS_WARNING_ASSERTION(!XRE_IsContentProcess(), |
431 | 0 | "No launching of new processes in the content process"); |
432 | 0 |
|
433 | 0 | if (NS_WARN_IF(!mExecutable)) { |
434 | 0 | return NS_ERROR_NOT_INITIALIZED; |
435 | 0 | } |
436 | 0 | if (NS_WARN_IF(mThread)) { |
437 | 0 | return NS_ERROR_ALREADY_INITIALIZED; |
438 | 0 | } |
439 | 0 | |
440 | 0 | if (aObserver) { |
441 | 0 | if (aHoldWeak) { |
442 | 0 | mWeakObserver = do_GetWeakReference(aObserver); |
443 | 0 | if (!mWeakObserver) { |
444 | 0 | return NS_NOINTERFACE; |
445 | 0 | } |
446 | 0 | } else { |
447 | 0 | mObserver = aObserver; |
448 | 0 | } |
449 | 0 | } |
450 | 0 |
|
451 | 0 | mExitValue = -1; |
452 | 0 | mPid = -1; |
453 | 0 |
|
454 | | #if defined(PROCESSMODEL_WINAPI) |
455 | | BOOL retVal; |
456 | | UniqueFreePtr<wchar_t> cmdLine; |
457 | | |
458 | | // |aMyArgv| is null-terminated and always starts with the program path. If |
459 | | // the second slot is non-null then arguments are being passed. |
460 | | if (aMyArgv[1] || mNoShell) { |
461 | | // Pass the executable path as argv[0] to the launched program when calling |
462 | | // CreateProcess(). |
463 | | char** argv = mNoShell ? aMyArgv : aMyArgv + 1; |
464 | | |
465 | | wchar_t* assembledCmdLine = nullptr; |
466 | | if (assembleCmdLine(argv, &assembledCmdLine, |
467 | | aArgsUTF8 ? CP_UTF8 : CP_ACP) == -1) { |
468 | | return NS_ERROR_FILE_EXECUTION_FAILED; |
469 | | } |
470 | | cmdLine.reset(assembledCmdLine); |
471 | | } |
472 | | |
473 | | // The program name in aMyArgv[0] is always UTF-8 |
474 | | NS_ConvertUTF8toUTF16 wideFile(aMyArgv[0]); |
475 | | |
476 | | if (mNoShell) { |
477 | | STARTUPINFO startupInfo; |
478 | | ZeroMemory(&startupInfo, sizeof(startupInfo)); |
479 | | startupInfo.cb = sizeof(startupInfo); |
480 | | startupInfo.dwFlags = STARTF_USESHOWWINDOW; |
481 | | startupInfo.wShowWindow = mStartHidden ? SW_HIDE : SW_SHOWNORMAL; |
482 | | |
483 | | PROCESS_INFORMATION processInfo; |
484 | | retVal = CreateProcess(/* lpApplicationName = */ wideFile.get(), |
485 | | /* lpCommandLine */ cmdLine.get(), |
486 | | /* lpProcessAttributes = */ NULL, |
487 | | /* lpThreadAttributes = */ NULL, |
488 | | /* bInheritHandles = */ FALSE, |
489 | | /* dwCreationFlags = */ 0, |
490 | | /* lpEnvironment = */ NULL, |
491 | | /* lpCurrentDirectory = */ NULL, |
492 | | /* lpStartupInfo = */ &startupInfo, |
493 | | /* lpProcessInformation */ &processInfo); |
494 | | |
495 | | if (!retVal) { |
496 | | return NS_ERROR_FILE_EXECUTION_FAILED; |
497 | | } |
498 | | |
499 | | CloseHandle(processInfo.hThread); |
500 | | |
501 | | mProcess = processInfo.hProcess; |
502 | | } else { |
503 | | SHELLEXECUTEINFOW sinfo; |
504 | | memset(&sinfo, 0, sizeof(SHELLEXECUTEINFOW)); |
505 | | sinfo.cbSize = sizeof(SHELLEXECUTEINFOW); |
506 | | sinfo.hwnd = nullptr; |
507 | | sinfo.lpFile = wideFile.get(); |
508 | | sinfo.nShow = mStartHidden ? SW_HIDE : SW_SHOWNORMAL; |
509 | | |
510 | | /* The SEE_MASK_NO_CONSOLE flag is important to prevent console windows |
511 | | * from appearing. This makes behavior the same on all platforms. The flag |
512 | | * will not have any effect on non-console applications. |
513 | | */ |
514 | | sinfo.fMask = SEE_MASK_FLAG_DDEWAIT | |
515 | | SEE_MASK_NO_CONSOLE | |
516 | | SEE_MASK_NOCLOSEPROCESS; |
517 | | |
518 | | if (cmdLine) { |
519 | | sinfo.lpParameters = cmdLine.get(); |
520 | | } |
521 | | |
522 | | retVal = ShellExecuteExW(&sinfo); |
523 | | if (!retVal) { |
524 | | return NS_ERROR_FILE_EXECUTION_FAILED; |
525 | | } |
526 | | |
527 | | mProcess = sinfo.hProcess; |
528 | | } |
529 | | |
530 | | mPid = GetProcessId(mProcess); |
531 | | #elif defined(XP_MACOSX) |
532 | | // Note: |aMyArgv| is already null-terminated as required by posix_spawnp. |
533 | | pid_t newPid = 0; |
534 | | int result = posix_spawnp(&newPid, aMyArgv[0], nullptr, nullptr, aMyArgv, |
535 | | *_NSGetEnviron()); |
536 | | mPid = static_cast<int32_t>(newPid); |
537 | | |
538 | | if (result != 0) { |
539 | | return NS_ERROR_FAILURE; |
540 | | } |
541 | | #elif defined(XP_UNIX) |
542 | | base::LaunchOptions options; |
543 | 0 | std::vector<std::string> argvVec; |
544 | 0 | for (char** arg = aMyArgv; *arg != nullptr; ++arg) { |
545 | 0 | argvVec.push_back(*arg); |
546 | 0 | } |
547 | 0 | pid_t newPid; |
548 | 0 | if (base::LaunchApp(argvVec, options, &newPid)) { |
549 | 0 | static_assert(sizeof(pid_t) <= sizeof(int32_t), |
550 | 0 | "mPid is large enough to hold a pid"); |
551 | 0 | mPid = static_cast<int32_t>(newPid); |
552 | 0 | } else { |
553 | 0 | return NS_ERROR_FAILURE; |
554 | 0 | } |
555 | | #else |
556 | | mProcess = PR_CreateProcess(aMyArgv[0], aMyArgv, nullptr, nullptr); |
557 | | if (!mProcess) { |
558 | | return NS_ERROR_FAILURE; |
559 | | } |
560 | | struct MYProcess |
561 | | { |
562 | | uint32_t pid; |
563 | | }; |
564 | | MYProcess* ptrProc = (MYProcess*)mProcess; |
565 | | mPid = ptrProc->pid; |
566 | | #endif |
567 | | |
568 | 0 | NS_ADDREF_THIS(); |
569 | 0 | mBlocking = aBlocking; |
570 | 0 | if (aBlocking) { |
571 | 0 | Monitor(this); |
572 | 0 | if (mExitValue < 0) { |
573 | 0 | return NS_ERROR_FILE_EXECUTION_FAILED; |
574 | 0 | } |
575 | 0 | } else { |
576 | 0 | mThread = PR_CreateThread(PR_SYSTEM_THREAD, Monitor, this, |
577 | 0 | PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, |
578 | 0 | PR_JOINABLE_THREAD, 0); |
579 | 0 | if (!mThread) { |
580 | 0 | NS_RELEASE_THIS(); |
581 | 0 | return NS_ERROR_FAILURE; |
582 | 0 | } |
583 | 0 |
|
584 | 0 | // It isn't a failure if we just can't watch for shutdown |
585 | 0 | nsCOMPtr<nsIObserverService> os = |
586 | 0 | mozilla::services::GetObserverService(); |
587 | 0 | if (os) { |
588 | 0 | os->AddObserver(this, "xpcom-shutdown", false); |
589 | 0 | } |
590 | 0 | } |
591 | 0 |
|
592 | 0 | return NS_OK; |
593 | 0 | } |
594 | | |
595 | | NS_IMETHODIMP |
596 | | nsProcess::GetIsRunning(bool* aIsRunning) |
597 | 0 | { |
598 | 0 | if (mThread) { |
599 | 0 | *aIsRunning = true; |
600 | 0 | } else { |
601 | 0 | *aIsRunning = false; |
602 | 0 | } |
603 | 0 |
|
604 | 0 | return NS_OK; |
605 | 0 | } |
606 | | |
607 | | NS_IMETHODIMP |
608 | | nsProcess::GetStartHidden(bool* aStartHidden) |
609 | 0 | { |
610 | 0 | *aStartHidden = mStartHidden; |
611 | 0 | return NS_OK; |
612 | 0 | } |
613 | | |
614 | | NS_IMETHODIMP |
615 | | nsProcess::SetStartHidden(bool aStartHidden) |
616 | 0 | { |
617 | 0 | mStartHidden = aStartHidden; |
618 | 0 | return NS_OK; |
619 | 0 | } |
620 | | |
621 | | NS_IMETHODIMP |
622 | | nsProcess::GetNoShell(bool* aNoShell) |
623 | 0 | { |
624 | 0 | *aNoShell = mNoShell; |
625 | 0 | return NS_OK; |
626 | 0 | } |
627 | | |
628 | | NS_IMETHODIMP |
629 | | nsProcess::SetNoShell(bool aNoShell) |
630 | 0 | { |
631 | 0 | mNoShell = aNoShell; |
632 | 0 | return NS_OK; |
633 | 0 | } |
634 | | |
635 | | NS_IMETHODIMP |
636 | | nsProcess::GetPid(uint32_t* aPid) |
637 | 0 | { |
638 | 0 | if (!mThread) { |
639 | 0 | return NS_ERROR_FAILURE; |
640 | 0 | } |
641 | 0 | if (mPid < 0) { |
642 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
643 | 0 | } |
644 | 0 | *aPid = mPid; |
645 | 0 | return NS_OK; |
646 | 0 | } |
647 | | |
648 | | NS_IMETHODIMP |
649 | | nsProcess::Kill() |
650 | 0 | { |
651 | 0 | if (!mThread) { |
652 | 0 | return NS_ERROR_FAILURE; |
653 | 0 | } |
654 | 0 | |
655 | 0 | { |
656 | 0 | MutexAutoLock lock(mLock); |
657 | | #if defined(PROCESSMODEL_WINAPI) |
658 | | if (TerminateProcess(mProcess, 0) == 0) { |
659 | | return NS_ERROR_FAILURE; |
660 | | } |
661 | | #elif defined(XP_UNIX) |
662 | 0 | if (kill(mPid, SIGKILL) != 0) { |
663 | 0 | return NS_ERROR_FAILURE; |
664 | 0 | } |
665 | | #else |
666 | | if (!mProcess || (PR_KillProcess(mProcess) != PR_SUCCESS)) { |
667 | | return NS_ERROR_FAILURE; |
668 | | } |
669 | | #endif |
670 | | } |
671 | 0 | |
672 | 0 | // We must null out mThread if we want IsRunning to return false immediately |
673 | 0 | // after this call. |
674 | 0 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
675 | 0 | if (os) { |
676 | 0 | os->RemoveObserver(this, "xpcom-shutdown"); |
677 | 0 | } |
678 | 0 | PR_JoinThread(mThread); |
679 | 0 | mThread = nullptr; |
680 | 0 |
|
681 | 0 | return NS_OK; |
682 | 0 | } |
683 | | |
684 | | NS_IMETHODIMP |
685 | | nsProcess::GetExitValue(int32_t* aExitValue) |
686 | 0 | { |
687 | 0 | MutexAutoLock lock(mLock); |
688 | 0 |
|
689 | 0 | *aExitValue = mExitValue; |
690 | 0 |
|
691 | 0 | return NS_OK; |
692 | 0 | } |
693 | | |
694 | | NS_IMETHODIMP |
695 | | nsProcess::Observe(nsISupports* aSubject, const char* aTopic, |
696 | | const char16_t* aData) |
697 | 0 | { |
698 | 0 | // Shutting down, drop all references |
699 | 0 | if (mThread) { |
700 | 0 | nsCOMPtr<nsIObserverService> os = |
701 | 0 | mozilla::services::GetObserverService(); |
702 | 0 | if (os) { |
703 | 0 | os->RemoveObserver(this, "xpcom-shutdown"); |
704 | 0 | } |
705 | 0 | mThread = nullptr; |
706 | 0 | } |
707 | 0 |
|
708 | 0 | mObserver = nullptr; |
709 | 0 | mWeakObserver = nullptr; |
710 | 0 |
|
711 | 0 | MutexAutoLock lock(mLock); |
712 | 0 | mShutdown = true; |
713 | 0 |
|
714 | 0 | return NS_OK; |
715 | 0 | } |