/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/***************************************************************************** * * nsProcess is used to execute new processes and specify if you want to * wait (blocking) or continue (non-blocking). * *****************************************************************************
*/
#ifdefined(PROCESSMODEL_WINAPI) # include "nsString.h" # include "nsLiteralString.h" # include "nsReadableUtils.h" # include "mozilla/AssembleCmdLine.h" # include "mozilla/UniquePtrExtensions.h" #else # ifdef XP_MACOSX # include <crt_externs.h> # include <spawn.h> # endif # ifdef XP_UNIX # ifndef XP_MACOSX # include "base/process_util.h" # endif # include <sys/wait.h> # include <sys/errno.h> # endif # include <sys/types.h> # include <signal.h> #endif
NS_IMETHODIMP
nsProcess::Init(nsIFile* aExecutable) { if (mExecutable) { return NS_ERROR_ALREADY_INITIALIZED;
}
if (NS_WARN_IF(!aExecutable)) { return NS_ERROR_INVALID_ARG;
} bool isFile;
// First make sure the file exists
nsresult rv = aExecutable->IsFile(&isFile); if (NS_FAILED(rv)) { return rv;
} if (!isFile) { return NS_ERROR_FAILURE;
}
// Store the nsIFile in mExecutable
mExecutable = aExecutable; // Get the path because it is needed by the NSPR process creation #ifdef XP_WIN
rv = mExecutable->GetTarget(mTargetPath); if (NS_FAILED(rv) || mTargetPath.IsEmpty()) #endif
rv = mExecutable->GetPath(mTargetPath);
return rv;
}
void nsProcess::Monitor(void* aArg) {
RefPtr<nsProcess> process = dont_AddRef(static_cast<nsProcess*>(aArg));
if (!process->mBlocking) {
NS_SetCurrentThreadName("RunProcess");
}
#ifdefined(PROCESSMODEL_WINAPI)
HANDLE processHandle;
{ // The mutex region cannot include WaitForSingleObject otherwise we'll // block calls such as Kill. So lock on access and store a local.
MutexAutoLock lock(process->mLock);
processHandle = process->mProcess;
}
DWORD dwRetVal; unsignedlong exitCode = -1;
dwRetVal = WaitForSingleObject(processHandle, INFINITE); if (dwRetVal != WAIT_FAILED) { if (GetExitCodeProcess(processHandle, &exitCode) == FALSE) {
exitCode = -1;
}
}
// Lock in case Kill or GetExitCode are called during this.
{
MutexAutoLock lock(process->mLock);
CloseHandle(process->mProcess);
process->mProcess = nullptr;
process->mExitValue = exitCode; if (process->mShutdown) { return;
}
} #else # ifdef XP_UNIX int exitCode = -1; int status = 0;
pid_t result; do {
result = waitpid(process->mPid, &status, 0);
} while (result == -1 && errno == EINTR); if (result == process->mPid) { if (WIFEXITED(status)) {
exitCode = WEXITSTATUS(status);
} elseif (WIFSIGNALED(status)) {
exitCode = 256; // match NSPR's signal exit status
}
} # else
int32_t exitCode = -1;
PRProcess* prProcess;
{ // The mutex region cannot include PR_WaitProcess otherwise we'll // block calls such as Kill. So lock on access and store a local.
MutexAutoLock lock(process->mLock);
prProcess = process->mProcess;
} if (PR_WaitProcess(prProcess, &exitCode) != PR_SUCCESS) {
exitCode = -1;
} # endif
// Lock in case Kill or GetExitCode are called during this
{
MutexAutoLock lock(process->mLock); # if !defined(XP_UNIX)
process->mProcess = nullptr; # endif
process->mExitValue = exitCode; if (process->mShutdown) { return;
}
} #endif
// If we ran a background thread for the monitor then notify on the main // thread if (NS_IsMainThread()) {
process->ProcessComplete();
} else {
NS_DispatchToMainThread(NewRunnableMethod( "nsProcess::ProcessComplete", process, &nsProcess::ProcessComplete));
}
}
void nsProcess::ProcessComplete() { if (mThread) {
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) {
os->RemoveObserver(this, "xpcom-shutdown");
}
PR_JoinThread(mThread);
mThread = nullptr;
}
nsresult nsProcess::CopyArgsAndRunProcess(bool aBlocking, constchar** aArgs,
uint32_t aCount,
nsIObserver* aObserver, bool aHoldWeak) { // Add one to the aCount for the program name and one for null termination. char** my_argv = nullptr;
my_argv = (char**)moz_xmalloc(sizeof(char*) * (aCount + 2));
my_argv[0] = ToNewUTF8String(mTargetPath);
for (uint32_t i = 0; i < aCount; ++i) {
my_argv[i + 1] = const_cast<char*>(aArgs[i]);
}
nsresult nsProcess::CopyArgsAndRunProcessw(bool aBlocking, const char16_t** aArgs,
uint32_t aCount,
nsIObserver* aObserver, bool aHoldWeak) { // Add one to the aCount for the program name and one for null termination. char** my_argv = nullptr;
my_argv = (char**)moz_xmalloc(sizeof(char*) * (aCount + 2));
my_argv[0] = ToNewUTF8String(mTargetPath);
for (uint32_t i = 0; i < aCount; i++) {
my_argv[i + 1] = ToNewUTF8String(nsDependentString(aArgs[i]));
}
for (uint32_t i = 0; i <= aCount; ++i) {
free(my_argv[i]);
}
free(my_argv); return rv;
}
nsresult nsProcess::RunProcess(bool aBlocking, char** aMyArgv,
nsIObserver* aObserver, bool aHoldWeak, bool aArgsUTF8) {
NS_WARNING_ASSERTION(!XRE_IsContentProcess(), "No launching of new processes in the content process");
if (NS_WARN_IF(!mExecutable)) { return NS_ERROR_NOT_INITIALIZED;
} if (NS_WARN_IF(mThread)) { return NS_ERROR_ALREADY_INITIALIZED;
}
// |aMyArgv| is null-terminated and always starts with the program path. If // the second slot is non-null then arguments are being passed. if (aMyArgv[1] || mNoShell) { // Pass the executable path as argv[0] to the launched program when calling // CreateProcess(). char** argv = mNoShell ? aMyArgv : aMyArgv + 1;
if (!retVal) { return NS_ERROR_FILE_EXECUTION_FAILED;
}
CloseHandle(processInfo.hThread);
// TODO(bug 1763051): assess if we need further work around this locking.
MutexAutoLock lock(mLock);
mProcess = processInfo.hProcess;
} else {
SHELLEXECUTEINFOW sinfo;
memset(&sinfo, 0, sizeof(SHELLEXECUTEINFOW));
sinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
sinfo.hwnd = nullptr;
sinfo.lpFile = wideFile.get();
sinfo.nShow = mStartHidden ? SW_HIDE : SW_SHOWNORMAL;
/* The SEE_MASK_NO_CONSOLE flag is important to prevent console windows * from appearing. This makes behavior the same on all platforms. The flag * will not have any effect on non-console applications.
*/
sinfo.fMask =
SEE_MASK_FLAG_DDEWAIT | SEE_MASK_NO_CONSOLE | SEE_MASK_NOCLOSEPROCESS;
if (cmdLine) {
sinfo.lpParameters = cmdLine.get();
}
retVal = ShellExecuteExW(&sinfo); if (!retVal) { return NS_ERROR_FILE_EXECUTION_FAILED;
}
// It isn't a failure if we just can't watch for shutdown
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) {
os->AddObserver(this, "xpcom-shutdown", false);
}
}
return NS_OK;
}
// We don't guarantee that monitor threads are joined before Gecko exits, which // can cause TSAN to complain about thread leaks. We handle this with a TSAN // suppression, and route thread creation through this helper so that the // suppression is as narrowly-scoped as possible.
PRThread* nsProcess::CreateMonitorThread() { return PR_CreateThread(PR_SYSTEM_THREAD, Monitor, this, PR_PRIORITY_NORMAL,
PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
}
// We must null out mThread if we want IsRunning to return false immediately // after this call.
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) {
os->RemoveObserver(this, "xpcom-shutdown");
}
PR_JoinThread(mThread);
mThread = nullptr;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.