Bug 1401786 - Move base::LaunchApp options into a LaunchOptions struct, like upstream Chromium. r=billm

MozReview-Commit-ID: 74IXV4oGeWR

--HG--
extra : rebase_source : ef4021a60506a8fc5fa5a35e3f3fefb9dbad75d6
This commit is contained in:
Jed Davis 2017-09-08 20:35:06 -06:00
parent 9c5a18de90
commit c7f8a32bf4
9 changed files with 116 additions and 124 deletions

View File

@ -95,16 +95,36 @@ void SetAllFDsToCloseOnExec();
// given multimap. Only call this function in a child process where you know
// that there aren't any other threads.
void CloseSuperfluousFds(const base::InjectiveMultimap& saved_map);
typedef std::vector<std::pair<int, int> > file_handle_mapping_vector;
typedef std::map<std::string, std::string> environment_map;
#endif
struct LaunchOptions {
// If true, wait for the process to terminate. Otherwise, return
// immediately.
bool wait = false;
#if defined(OS_WIN)
bool start_hidden = false;
#endif
#if defined(OS_POSIX)
// Environment variables to be applied in addition to the current
// process's environment, replacing them where necessary.
environment_map environ;
// A mapping of (src fd -> dest fd) to propagate into the child
// process. All other fds will be closed, except std{in,out,err}.
file_handle_mapping_vector fds_to_remap;
#endif
};
#if defined(OS_WIN)
// Runs the given application name with the given command line. Normally, the
// first command line argument should be the path to the process, and don't
// forget to quote it.
//
// If wait is true, it will block and wait for the other process to finish,
// otherwise, it will just continue asynchronously.
//
// Example (including literal quotes)
// cmdline = "c:\windows\explorer.exe" -foo "c:\bar\"
//
@ -113,28 +133,20 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_map);
// NOTE: In this case, the caller is responsible for closing the handle so
// that it doesn't leak!
bool LaunchApp(const std::wstring& cmdline,
bool wait, bool start_hidden, ProcessHandle* process_handle);
const LaunchOptions& options,
ProcessHandle* process_handle);
#elif defined(OS_POSIX)
// Runs the application specified in argv[0] with the command line argv.
// Before launching all FDs open in the parent process will be marked as
// close-on-exec. |fds_to_remap| defines a mapping of src fd->dest fd to
// propagate FDs into the child process.
//
// As above, if wait is true, execute synchronously. The pid will be stored
// in process_handle if that pointer is non-null.
// The pid will be stored in process_handle if that pointer is
// non-null.
//
// Note that the first argument in argv must point to the filename,
// and must be fully specified.
typedef std::vector<std::pair<int, int> > file_handle_mapping_vector;
// and must be fully specified (i.e., this will not search $PATH).
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
bool wait, ProcessHandle* process_handle);
typedef std::map<std::string, std::string> environment_map;
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
const environment_map& env_vars_to_set,
bool wait, ProcessHandle* process_handle);
const LaunchOptions& options,
ProcessHandle* process_handle);
// Deleter for the array of strings allocated within BuildEnvironmentArray.
struct FreeEnvVarsArray
@ -152,7 +164,8 @@ EnvironmentArray BuildEnvironmentArray(const environment_map& env_vars_to_set);
// Executes the application specified by cl. This function delegates to one
// of the above two platform-specific functions.
bool LaunchApp(const CommandLine& cl,
bool wait, bool start_hidden, ProcessHandle* process_handle);
const LaunchOptions&,
ProcessHandle* process_handle);
// Used to filter processes by process ID.
class ProcessFilter {

View File

@ -24,17 +24,11 @@ static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
namespace base {
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
bool wait, ProcessHandle* process_handle) {
return LaunchApp(argv, fds_to_remap, environment_map(),
wait, process_handle);
}
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
const environment_map& env_vars_to_set,
bool wait, ProcessHandle* process_handle) {
const LaunchOptions& options,
ProcessHandle* process_handle)
{
bool retval = true;
char* argv_copy[argv.size() + 1];
@ -47,7 +41,7 @@ bool LaunchApp(const std::vector<std::string>& argv,
// as close-on-exec.
SetAllFDsToCloseOnExec();
EnvironmentArray vars = BuildEnvironmentArray(env_vars_to_set);
EnvironmentArray vars = BuildEnvironmentArray(options.environ);
posix_spawn_file_actions_t file_actions;
if (posix_spawn_file_actions_init(&file_actions) != 0) {
@ -55,11 +49,9 @@ bool LaunchApp(const std::vector<std::string>& argv,
}
// Turn fds_to_remap array into a set of dup2 calls.
for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin();
it != fds_to_remap.end();
++it) {
int src_fd = it->first;
int dest_fd = it->second;
for (const auto& fd_map : options.fds_to_remap) {
int src_fd = fd_map.first;
int dest_fd = fd_map.second;
if (src_fd == dest_fd) {
int flags = fcntl(src_fd, F_GETFD);
@ -90,7 +82,7 @@ bool LaunchApp(const std::vector<std::string>& argv,
} else {
gProcessLog.print("==> process %d launched child process %d\n",
GetCurrentProcId(), pid);
if (wait)
if (options.wait)
HANDLE_EINTR(waitpid(pid, 0, 0));
if (process_handle)
@ -101,10 +93,9 @@ bool LaunchApp(const std::vector<std::string>& argv,
}
bool LaunchApp(const CommandLine& cl,
bool wait, bool start_hidden, ProcessHandle* process_handle) {
// TODO(playmobil): Do we need to respect the start_hidden flag?
file_handle_mapping_vector no_files;
return LaunchApp(cl.argv(), no_files, wait, process_handle);
const LaunchOptions& options,
ProcessHandle* process_handle) {
return LaunchApp(cl.argv(), options, process_handle);
}
} // namespace base

View File

@ -25,33 +25,26 @@ static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
namespace base {
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
bool wait, ProcessHandle* process_handle) {
return LaunchApp(argv, fds_to_remap, environment_map(),
wait, process_handle);
}
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
const environment_map& env_vars_to_set,
bool wait, ProcessHandle* process_handle) {
const LaunchOptions& options,
ProcessHandle* process_handle)
{
mozilla::UniquePtr<char*[]> argv_cstr(new char*[argv.size() + 1]);
// Illegal to allocate memory after fork and before execvp
InjectiveMultimap fd_shuffle1, fd_shuffle2;
fd_shuffle1.reserve(fds_to_remap.size());
fd_shuffle2.reserve(fds_to_remap.size());
fd_shuffle1.reserve(options.fds_to_remap.size());
fd_shuffle2.reserve(options.fds_to_remap.size());
EnvironmentArray envp = BuildEnvironmentArray(env_vars_to_set);
EnvironmentArray envp = BuildEnvironmentArray(options.environ);
pid_t pid = fork();
if (pid < 0)
return false;
if (pid == 0) {
for (file_handle_mapping_vector::const_iterator
it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) {
fd_shuffle1.push_back(InjectionArc(it->first, it->second, false));
fd_shuffle2.push_back(InjectionArc(it->first, it->second, false));
// In the child:
for (const auto& fd_map : options.fds_to_remap) {
fd_shuffle1.push_back(InjectionArc(fd_map.first, fd_map.second, false));
fd_shuffle2.push_back(InjectionArc(fd_map.first, fd_map.second, false));
}
if (!ShuffleFileDescriptors(&fd_shuffle1))
@ -69,24 +62,24 @@ bool LaunchApp(const std::vector<std::string>& argv,
// only on debug builds; otherwise it's a signal-safe no-op.)
DLOG(ERROR) << "FAILED TO exec() CHILD PROCESS, path: " << argv_cstr[0];
_exit(127);
} else {
gProcessLog.print("==> process %d launched child process %d\n",
GetCurrentProcId(), pid);
if (wait)
HANDLE_EINTR(waitpid(pid, 0, 0));
if (process_handle)
*process_handle = pid;
}
// In the parent:
gProcessLog.print("==> process %d launched child process %d\n",
GetCurrentProcId(), pid);
if (options.wait)
HANDLE_EINTR(waitpid(pid, 0, 0));
if (process_handle)
*process_handle = pid;
return true;
}
bool LaunchApp(const CommandLine& cl,
bool wait, bool start_hidden,
const LaunchOptions& options,
ProcessHandle* process_handle) {
file_handle_mapping_vector no_files;
return LaunchApp(cl.argv(), no_files, wait, process_handle);
return LaunchApp(cl.argv(), options, process_handle);
}
} // namespace base

View File

@ -1,8 +1,9 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process_util.h"
#include <fcntl.h>
@ -24,16 +25,9 @@ static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
namespace base {
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
bool wait, ProcessHandle* process_handle) {
return LaunchApp(argv, fds_to_remap, environment_map(),
wait, process_handle);
}
bool LaunchApp(const std::vector<std::string>& argv,
const file_handle_mapping_vector& fds_to_remap,
const environment_map& env_vars_to_set,
bool wait, ProcessHandle* process_handle) {
const LaunchOptions& options,
ProcessHandle* process_handle)
{
bool retval = true;
char* argv_copy[argv.size() + 1];
@ -42,7 +36,7 @@ bool LaunchApp(const std::vector<std::string>& argv,
}
argv_copy[argv.size()] = NULL;
EnvironmentArray vars = BuildEnvironmentArray(env_vars_to_set);
EnvironmentArray vars = BuildEnvironmentArray(options.environ);
posix_spawn_file_actions_t file_actions;
if (posix_spawn_file_actions_init(&file_actions) != 0) {
@ -53,11 +47,9 @@ bool LaunchApp(const std::vector<std::string>& argv,
});
// Turn fds_to_remap array into a set of dup2 calls.
for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin();
it != fds_to_remap.end();
++it) {
int src_fd = it->first;
int dest_fd = it->second;
for (const auto& fd_map : options.fds_to_remap) {
int src_fd = fd_map.first;
int dest_fd = fd_map.second;
if (src_fd == dest_fd) {
int flags = fcntl(src_fd, F_GETFD);
@ -71,6 +63,7 @@ bool LaunchApp(const std::vector<std::string>& argv,
}
}
// Initialize spawn attributes.
posix_spawnattr_t spawnattr;
if (posix_spawnattr_init(&spawnattr) != 0) {
return false;
@ -107,7 +100,7 @@ bool LaunchApp(const std::vector<std::string>& argv,
} else {
gProcessLog.print("==> process %d launched child process %d\n",
GetCurrentProcId(), pid);
if (wait)
if (options.wait)
HANDLE_EINTR(waitpid(pid, 0, 0));
if (process_handle)
@ -118,10 +111,9 @@ bool LaunchApp(const std::vector<std::string>& argv,
}
bool LaunchApp(const CommandLine& cl,
bool wait, bool start_hidden, ProcessHandle* process_handle) {
// TODO(playmobil): Do we need to respect the start_hidden flag?
file_handle_mapping_vector no_files;
return LaunchApp(cl.argv(), no_files, wait, process_handle);
const LaunchOptions& options,
ProcessHandle* process_handle) {
return LaunchApp(cl.argv(), options, process_handle);
}
} // namespace base

View File

@ -272,7 +272,8 @@ void FreeThreadAttributeList(LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList) {
}
bool LaunchApp(const std::wstring& cmdline,
bool wait, bool start_hidden, ProcessHandle* process_handle) {
const LaunchOptions& options,
ProcessHandle* process_handle) {
// We want to inherit the std handles so dump() statements and assertion
// messages in the child process can be seen - but we *do not* want to
@ -289,7 +290,7 @@ bool LaunchApp(const std::wstring& cmdline,
STARTUPINFO &startup_info = startup_info_ex.StartupInfo;
startup_info.cb = sizeof(startup_info);
startup_info.dwFlags = STARTF_USESHOWWINDOW;
startup_info.wShowWindow = start_hidden ? SW_HIDE : SW_SHOW;
startup_info.wShowWindow = options.start_hidden ? SW_HIDE : SW_SHOW;
// Per the comment in CreateThreadAttributeList, lpAttributeList will contain
// a pointer to handlesToInherit, so make sure they have the same lifetime.
@ -340,7 +341,7 @@ bool LaunchApp(const std::wstring& cmdline,
// Handles must be closed or they will leak
CloseHandle(process_info.hThread);
if (wait)
if (options.wait)
WaitForSingleObject(process_info.hProcess, INFINITE);
// If the caller wants the process handle, we won't close it.
@ -353,9 +354,9 @@ bool LaunchApp(const std::wstring& cmdline,
}
bool LaunchApp(const CommandLine& cl,
bool wait, bool start_hidden, ProcessHandle* process_handle) {
return LaunchApp(cl.command_line_string(), wait,
start_hidden, process_handle);
const LaunchOptions& options,
ProcessHandle* process_handle) {
return LaunchApp(cl.command_line_string(), options, process_handle);
}
bool KillProcess(ProcessHandle process, int exit_code, bool wait) {

View File

@ -86,6 +86,7 @@ GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType,
: mProcessType(aProcessType),
mIsFileContent(aIsFileContent),
mMonitor("mozilla.ipc.GeckChildProcessHost.mMonitor"),
mLaunchOptions(MakeUnique<base::LaunchOptions>()),
mProcessState(CREATING_CHANNEL),
#if defined(MOZ_SANDBOX) && defined(XP_WIN)
mEnableSandboxLogging(false),
@ -642,17 +643,16 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
// we split the logic here.
# if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD) || defined(OS_SOLARIS)
base::environment_map newEnvVars;
# if defined(MOZ_WIDGET_GTK)
if (mProcessType == GeckoProcessType_Content) {
// disable IM module to avoid sandbox violation
newEnvVars["GTK_IM_MODULE"] = "gtk-im-context-simple";
mLaunchOptions->environ["GTK_IM_MODULE"] = "gtk-im-context-simple";
// Disable ATK accessibility code in content processes because it conflicts
// with the sandbox, and we proxy that information through the main process
// anyway.
newEnvVars["NO_AT_BRIDGE"] = "1";
mLaunchOptions->environ["NO_AT_BRIDGE"] = "1";
}
# endif // defined(MOZ_WIDGET_GTK)
@ -678,10 +678,10 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
new_ld_lib_path.Append(':');
new_ld_lib_path.Append(ld_library_path);
}
newEnvVars["LD_LIBRARY_PATH"] = new_ld_lib_path.get();
mLaunchOptions->environ["LD_LIBRARY_PATH"] = new_ld_lib_path.get();
# elif OS_MACOSX // defined(OS_LINUX) || defined(OS_BSD)
newEnvVars["DYLD_LIBRARY_PATH"] = path.get();
mLaunchOptions->environ["DYLD_LIBRARY_PATH"] = path.get();
// XXX DYLD_INSERT_LIBRARIES should only be set when launching a plugin
// process, and has no effect on other subprocesses (the hooks in
// libplugin_child_interpose.dylib become noops). But currently it
@ -700,7 +700,7 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
}
interpose.Append(path.get());
interpose.AppendLiteral("/libplugin_child_interpose.dylib");
newEnvVars["DYLD_INSERT_LIBRARIES"] = interpose.get();
mLaunchOptions->environ["DYLD_INSERT_LIBRARIES"] = interpose.get();
# endif // defined(OS_LINUX) || defined(OS_BSD)
}
# endif // defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD) || defined(OS_SOLARIS)
@ -725,7 +725,7 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
}
// Explicitly construct the std::string to make it clear that this
// isn't retaining a pointer to the nsCString's buffer.
newEnvVars["LD_PRELOAD"] = std::string(preload.get());
mLaunchOptions->environ["LD_PRELOAD"] = std::string(preload.get());
}
# endif // defined(XP_LINUX) && defined(MOZ_SANDBOX)
@ -733,7 +733,8 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
// STDOUT_FILENO, for example
int srcChannelFd, dstChannelFd;
channel().GetClientFileDescriptorMapping(&srcChannelFd, &dstChannelFd);
mFileMap.push_back(std::pair<int,int>(srcChannelFd, dstChannelFd));
mLaunchOptions->fds_to_remap
.push_back(std::pair<int,int>(srcChannelFd, dstChannelFd));
// no need for kProcessChannelID, the child process inherits the
// other end of the socketpair() from us
@ -790,7 +791,8 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
&childCrashFd, &childCrashRemapFd))
return false;
if (0 <= childCrashFd) {
mFileMap.push_back(std::pair<int,int>(childCrashFd, childCrashRemapFd));
mLaunchOptions->fds_to_remap
.push_back(std::pair<int,int>(childCrashFd, childCrashRemapFd));
// "true" == crash reporting enabled
childArgv.push_back("true");
}
@ -808,7 +810,7 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
int srcFd, dstFd;
SandboxReporter::Singleton()
->GetClientFileDescriptorMapping(&srcFd, &dstFd);
mFileMap.push_back(std::make_pair(srcFd, dstFd));
mLaunchOptions->fds_to_remap.push_back(std::make_pair(srcFd, dstFd));
}
# endif // defined(XP_LINUX) && defined(MOZ_SANDBOX)
@ -826,13 +828,10 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
childArgv.push_back(childProcessType);
# if defined(MOZ_WIDGET_ANDROID)
LaunchAndroidService(childProcessType, childArgv, mFileMap, &process);
LaunchAndroidService(childProcessType, childArgv,
mLaunchOptions->fds_to_remap, &process);
# else // goes with defined(MOZ_WIDGET_ANDROID)
base::LaunchApp(childArgv, mFileMap,
# if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD) || defined(OS_SOLARIS)
newEnvVars,
# endif // defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD) || defined(OS_SOLARIS)
false, &process);
base::LaunchApp(childArgv, *mLaunchOptions, &process);
# endif // defined(MOZ_WIDGET_ANDROID)
// We're in the parent and the child was launched. Close the child FD in the
@ -1064,7 +1063,7 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
} else
# endif // defined(XP_WIN) && defined(MOZ_SANDBOX)
{
base::LaunchApp(cmdLine, false, false, &process);
base::LaunchApp(cmdLine, *mLaunchOptions, &process);
# ifdef MOZ_SANDBOX
// We need to be able to duplicate handles to some types of non-sandboxed
@ -1111,6 +1110,7 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
mProcessState = PROCESS_CREATED;
lock.Notify();
mLaunchOptions = nullptr;
return true;
}

View File

@ -16,6 +16,7 @@
#include "mozilla/ipc/FileDescriptor.h"
#include "mozilla/Monitor.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/UniquePtr.h"
#include "nsCOMPtr.h"
#include "nsXULAppAPI.h" // for GeckoProcessType
@ -118,6 +119,11 @@ protected:
bool mIsFileContent;
Monitor mMonitor;
FilePath mProcessPath;
// GeckoChildProcessHost holds the launch options so they can be set
// up on the main thread using main-thread-only APIs like prefs, and
// then used for the actual launch on another thread. This pointer
// is set to null to free the options after the child is launched.
UniquePtr<base::LaunchOptions> mLaunchOptions;
// This value must be accessed while holding mMonitor.
enum {
@ -152,10 +158,6 @@ protected:
#endif
#endif // XP_WIN
#if defined(OS_POSIX)
base::file_handle_mapping_vector mFileMap;
#endif
ProcessHandle mChildProcessHandle;
#if defined(OS_MACOSX)
task_t mChildTask;

View File

@ -34,14 +34,14 @@ GetLSBRelease(nsACString& aDistributor,
gLsbReleasePath, "-idrc"
};
std::vector<std::pair<int, int>> fdMap = {
{ pipefd[1], STDOUT_FILENO }
};
base::LaunchOptions options;
options.fds_to_remap.push_back({ pipefd[1], STDOUT_FILENO });
options.wait = true;
base::ProcessHandle process;
base::LaunchApp(argv, fdMap, true, &process);
bool ok = base::LaunchApp(argv, options, &process);
close(pipefd[1]);
if (!process) {
if (!ok) {
NS_WARNING("Failed to spawn lsb_release!");
close(pipefd[0]);
return false;

View File

@ -576,13 +576,13 @@ nsProcess::RunProcess(bool aBlocking, char** aMyArgv, nsIObserver* aObserver,
return NS_ERROR_FAILURE;
}
#elif defined(XP_UNIX)
base::file_handle_mapping_vector fdMap;
base::LaunchOptions options;
std::vector<std::string> argvVec;
for (char** arg = aMyArgv; *arg != nullptr; ++arg) {
argvVec.push_back(*arg);
}
pid_t newPid;
if (base::LaunchApp(argvVec, fdMap, false, &newPid)) {
if (base::LaunchApp(argvVec, options, &newPid)) {
static_assert(sizeof(pid_t) <= sizeof(int32_t),
"mPid is large enough to hold a pid");
mPid = static_cast<int32_t>(newPid);