diff options
-rw-r--r-- | base/process_util.h | 256 | ||||
-rw-r--r-- | base/process_util_posix.cc | 77 | ||||
-rw-r--r-- | base/process_util_win.cc | 107 |
3 files changed, 243 insertions, 197 deletions
diff --git a/base/process_util.h b/base/process_util.h index 3e758d4..0575903 100644 --- a/base/process_util.h +++ b/base/process_util.h @@ -190,6 +190,79 @@ BASE_API ProcessId GetParentProcessId(ProcessHandle process); BASE_API void CloseSuperfluousFds(const InjectiveMultimap& saved_map); #endif +// TODO(evan): rename these to use StudlyCaps. +typedef std::vector<std::pair<std::string, std::string> > environment_vector; +typedef std::vector<std::pair<int, int> > file_handle_mapping_vector; + +// Options for launching a subprocess that are passed to LaunchApp(). +struct LaunchOptions { + LaunchOptions() : wait(false), process_handle(NULL), +#if defined(OS_WIN) + start_hidden(false), inherit_handles(false), as_user(NULL), + empty_desktop_name(false) +#else + environ(NULL), fds_to_remap(NULL), new_process_group(false) +#endif + {} + + // If true, wait for the process to complete. + bool wait; + + // If non-NULL, will be filled in with the handle of the launched process. + // NOTE: In this case, the caller is responsible for closing the handle so + // that it doesn't leak! Otherwise, the handle will be implicitly + // closed. + // Not especially useful unless |wait| is false. + ProcessHandle* process_handle; + +#if defined(OS_WIN) + bool start_hidden; + + // If true, the new process inherits handles from the parent. + bool inherit_handles; + + // If non-NULL, runs as if the user represented by the token had launched it. + // Whether the application is visible on the interactive desktop depends on + // the token belonging to an interactive logon session. + // + // To avoid hard to diagnose problems, when specified this loads the + // environment variables associated with the user and if this operation fails + // the entire call fails as well. + UserTokenHandle as_user; + + // If true, use an empty string for the desktop name. + bool empty_desktop_name; +#else + // If non-NULL, set/unset environment variables. + // See documentation of AlterEnvironment(). + // This pointer is owned by the caller and must live through the + // call to LaunchProcess(). + const environment_vector* environ; + + // If non-NULL, remap file descriptors according to the mapping of + // src fd->dest fd to propagate FDs into the child process. + // This pointer is owned by the caller and must live through the + // call to LaunchProcess(). + const file_handle_mapping_vector* fds_to_remap; + + // If true, start the process in a new process group, instead of + // inheriting the parent's process group. The pgid of the child process + // will be the same as its pid. + bool new_process_group; +#endif +}; + +// Launch a process via the command line |cmdline|. +// See the documentation of LaunchOptions for details on launching options. +// +// Unix-specific notes: +// - Before launching, all FDs open in the parent process will be marked as +// close-on-exec. +// - If the first argument on the command line does not contain a slash, +// PATH will be searched. (See man execvp.) +BASE_API bool LaunchProcess(const CommandLine& cmdline, + const LaunchOptions& options); + #if defined(OS_WIN) enum IntegrityLevel { @@ -204,87 +277,115 @@ enum IntegrityLevel { BASE_API bool GetProcessIntegrityLevel(ProcessHandle process, IntegrityLevel *level); -// 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. +// Windows-specific LaunchProcess that takes the command line as a +// string. Useful for situations where you need to control the +// command line arguments directly, but prefer the CommandLine version +// if launching Chrome itself. // -// If wait is true, it will block and wait for the other process to finish, -// otherwise, it will just continue asynchronously. +// The first command line argument should be the path to the process, +// and don't forget to quote it. // // Example (including literal quotes) // cmdline = "c:\windows\explorer.exe" -foo "c:\bar\" -// -// If process_handle is non-NULL, the process handle of the launched app will be -// stored there on a successful launch. -// NOTE: In this case, the caller is responsible for closing the handle so -// that it doesn't leak! -BASE_API bool LaunchApp(const std::wstring& cmdline, - bool wait, bool start_hidden, - ProcessHandle* process_handle); - -// Same as LaunchApp, except allows the new process to inherit handles of the -// parent process. -BASE_API bool LaunchAppWithHandleInheritance(const std::wstring& cmdline, - bool wait, bool start_hidden, - ProcessHandle* process_handle); - -// Runs the given application name with the given command line as if the user -// represented by |token| had launched it. The caveats about |cmdline| and -// |process_handle| explained for LaunchApp above apply as well. -// -// Whether the application is visible on the interactive desktop depends on -// the token belonging to an interactive logon session. -// -// To avoid hard to diagnose problems, this function internally loads the -// environment variables associated with the user and if this operation fails -// the entire call fails as well. -BASE_API bool LaunchAppAsUser(UserTokenHandle token, - const std::wstring& cmdline, - bool start_hidden, - ProcessHandle* process_handle); - -// Has the same behavior as LaunchAppAsUser, but offers the boolean option to -// use an empty string for the desktop name and a boolean for allowing the -// child process to inherit handles from its parent. -BASE_API bool LaunchAppAsUser(UserTokenHandle token, - const std::wstring& cmdline, - bool start_hidden, ProcessHandle* process_handle, - bool empty_desktop_name, bool inherit_handles); - +BASE_API bool LaunchProcess(const string16& cmdline, + const LaunchOptions& options); + +// TODO(evan): deprecated; change callers to use LaunchProcess, remove. +inline bool LaunchApp(const std::wstring& cmdline, + bool wait, bool start_hidden, + ProcessHandle* process_handle) { + LaunchOptions options; + options.wait = wait; + options.start_hidden = start_hidden; + options.process_handle = process_handle; + return LaunchProcess(cmdline, options); +} + +// TODO(evan): deprecated; change callers to use LaunchProcess, remove. +inline bool LaunchAppWithHandleInheritance(const std::wstring& cmdline, + bool wait, bool start_hidden, + ProcessHandle* process_handle) { + LaunchOptions options; + options.wait = wait; + options.start_hidden = start_hidden; + options.process_handle = process_handle; + options.inherit_handles = true; + return LaunchProcess(cmdline, options); +} + +// TODO(evan): deprecated; change callers to use LaunchProcess, remove. +inline bool LaunchAppAsUser(UserTokenHandle token, + const std::wstring& cmdline, + bool start_hidden, + ProcessHandle* process_handle) { + LaunchOptions options; + options.start_hidden = start_hidden; + options.process_handle = process_handle; + options.as_user = token; + return LaunchProcess(cmdline, options); +} + +// TODO(evan): deprecated; change callers to use LaunchProcess, remove. +inline bool LaunchAppAsUser(UserTokenHandle token, + const std::wstring& cmdline, + bool start_hidden, ProcessHandle* process_handle, + bool empty_desktop_name, bool inherit_handles) { + LaunchOptions options; + options.start_hidden = start_hidden; + options.process_handle = process_handle; + options.as_user = token; + options.empty_desktop_name = empty_desktop_name; + options.inherit_handles = inherit_handles; + return LaunchProcess(cmdline, options); +} #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. -// -// Note that the first argument in argv must point to the executable filename. -// If the filename is not fully specified, PATH will be searched. -typedef std::vector<std::pair<int, int> > file_handle_mapping_vector; -BASE_API bool LaunchApp(const std::vector<std::string>& argv, - const file_handle_mapping_vector& fds_to_remap, - bool wait, ProcessHandle* process_handle); - -// Similar to the above, but also (un)set environment variables in child process -// through |environ|. -typedef std::vector<std::pair<std::string, std::string> > environment_vector; -BASE_API bool LaunchApp(const std::vector<std::string>& argv, - const environment_vector& environ, - const file_handle_mapping_vector& fds_to_remap, - bool wait, ProcessHandle* process_handle); - -// Similar to the above two methods, but starts the child process in a process -// group of its own, instead of allowing it to inherit the parent's process -// group. The pgid of the child process will be the same as its pid. -BASE_API bool LaunchAppInNewProcessGroup( +// A POSIX-specific version of LaunchProcess that takes an argv array +// instead of a CommandLine. Useful for situations where you need to +// control the command line arguments directly, but prefer the +// CommandLine version if launching Chrome itself. +BASE_API bool LaunchProcess(const std::vector<std::string>& argv, + const LaunchOptions& options); + +// TODO(evan): deprecated; change callers to use LaunchProcess, remove. +inline bool LaunchApp(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + bool wait, ProcessHandle* process_handle) { + LaunchOptions options; + options.fds_to_remap = &fds_to_remap; + options.wait = wait; + options.process_handle = process_handle; + return LaunchProcess(argv, options); +} + +// TODO(evan): deprecated; change callers to use LaunchProcess, remove. +inline bool LaunchApp(const std::vector<std::string>& argv, + const environment_vector& environ, + const file_handle_mapping_vector& fds_to_remap, + bool wait, ProcessHandle* process_handle) { + LaunchOptions options; + options.environ = &environ; + options.fds_to_remap = &fds_to_remap; + options.wait = wait; + options.process_handle = process_handle; + return LaunchProcess(argv, options); +} + +// TODO(evan): deprecated; change callers to use LaunchProcess, remove. +inline bool LaunchAppInNewProcessGroup( const std::vector<std::string>& argv, const environment_vector& environ, const file_handle_mapping_vector& fds_to_remap, bool wait, - ProcessHandle* process_handle); + ProcessHandle* process_handle) { + LaunchOptions options; + options.environ = &environ; + options.fds_to_remap = &fds_to_remap; + options.wait = wait; + options.process_handle = process_handle; + options.new_process_group = true; + return LaunchProcess(argv, options); +} // AlterEnvironment returns a modified environment vector, constructed from the // given environment and the list of changes given in |changes|. Each key in @@ -298,9 +399,16 @@ BASE_API char** AlterEnvironment(const environment_vector& changes, #endif // defined(OS_POSIX) // Executes the application specified by cl. This function delegates to one -// of the above two platform-specific functions. -BASE_API bool LaunchApp(const CommandLine& cl, bool wait, bool start_hidden, - ProcessHandle* process_handle); +// of the above platform-specific functions. +// TODO(evan): deprecated; change callers to use LaunchProcess, remove. +inline bool LaunchApp(const CommandLine& cl, bool wait, bool start_hidden, + ProcessHandle* process_handle) { + LaunchOptions options; + options.wait = wait; + options.process_handle = process_handle; + + return LaunchProcess(cl, options); +} // Executes the application specified by |cl| and wait for it to exit. Stores // the output (stdout) in |output|. Redirects stderr to /dev/null. Returns true diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc index b495b43..43c16ea 100644 --- a/base/process_util_posix.cc +++ b/base/process_util_posix.cc @@ -527,20 +527,18 @@ char** AlterEnvironment(const environment_vector& changes, return ret; } -bool LaunchAppImpl( - const std::vector<std::string>& argv, - const environment_vector& env_changes, - const file_handle_mapping_vector& fds_to_remap, - bool wait, - ProcessHandle* process_handle, - bool start_new_process_group) { +bool LaunchProcess(const std::vector<std::string>& argv, + const LaunchOptions& options) { pid_t pid; InjectiveMultimap fd_shuffle1, fd_shuffle2; - fd_shuffle1.reserve(fds_to_remap.size()); - fd_shuffle2.reserve(fds_to_remap.size()); + if (options.fds_to_remap) { + fd_shuffle1.reserve(options.fds_to_remap->size()); + fd_shuffle2.reserve(options.fds_to_remap->size()); + } scoped_array<char*> argv_cstr(new char*[argv.size() + 1]); - scoped_array<char*> new_environ(AlterEnvironment(env_changes, - GetEnvironment())); + scoped_array<char*> new_environ; + if (options.environ) + new_environ.reset(AlterEnvironment(*options.environ, GetEnvironment())); pid = fork(); if (pid < 0) { @@ -572,7 +570,7 @@ bool LaunchAppImpl( _exit(127); } - if (start_new_process_group) { + if (options.new_process_group) { // Instead of inheriting the process group ID of the parent, the child // starts off a new process group with pgid equal to its process ID. if (setpgid(0, 0) < 0) { @@ -598,13 +596,17 @@ bool LaunchAppImpl( // DANGER: no calls to malloc are allowed from now on: // http://crbug.com/36678 - 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)); + if (options.fds_to_remap) { + for (file_handle_mapping_vector::const_iterator + it = options.fds_to_remap->begin(); + it != options.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)); + } } - SetEnvironment(new_environ.get()); + if (options.environ) + SetEnvironment(new_environ.get()); // fd_shuffle1 is mutated by this call because it cannot malloc. if (!ShuffleFileDescriptors(&fd_shuffle1)) @@ -621,7 +623,7 @@ bool LaunchAppImpl( _exit(127); } else { // Parent process - if (wait) { + if (options.wait) { // While this isn't strictly disk IO, waiting for another process to // finish is the sort of thing ThreadRestrictions is trying to prevent. base::ThreadRestrictions::AssertIOAllowed(); @@ -629,45 +631,16 @@ bool LaunchAppImpl( DPCHECK(ret > 0); } - if (process_handle) - *process_handle = pid; + if (options.process_handle) + *options.process_handle = pid; } return true; } -bool LaunchApp( - const std::vector<std::string>& argv, - const environment_vector& env_changes, - const file_handle_mapping_vector& fds_to_remap, - bool wait, - ProcessHandle* process_handle) { - return LaunchAppImpl(argv, env_changes, fds_to_remap, - wait, process_handle, false); -} - -bool LaunchAppInNewProcessGroup( - const std::vector<std::string>& argv, - const environment_vector& env_changes, - const file_handle_mapping_vector& fds_to_remap, - bool wait, - ProcessHandle* process_handle) { - return LaunchAppImpl(argv, env_changes, fds_to_remap, wait, - process_handle, true); -} - -bool LaunchApp(const std::vector<std::string>& argv, - const file_handle_mapping_vector& fds_to_remap, - bool wait, ProcessHandle* process_handle) { - base::environment_vector no_env; - return LaunchApp(argv, no_env, fds_to_remap, wait, process_handle); -} - -bool LaunchApp(const CommandLine& cl, - bool wait, bool start_hidden, - ProcessHandle* process_handle) { - file_handle_mapping_vector no_files; - return LaunchApp(cl.argv(), no_files, wait, process_handle); +bool LaunchProcess(const CommandLine& cmdline, + const LaunchOptions& options) { + return LaunchProcess(cmdline.argv(), options); } ProcessMetrics::~ProcessMetrics() { } diff --git a/base/process_util_win.cc b/base/process_util_win.cc index 5b07105..c11878e 100644 --- a/base/process_util_win.cc +++ b/base/process_util_win.cc @@ -217,94 +217,59 @@ bool GetProcessIntegrityLevel(ProcessHandle process, IntegrityLevel *level) { return true; } -bool LaunchAppImpl(const std::wstring& cmdline, - bool wait, bool start_hidden, bool inherit_handles, - ProcessHandle* process_handle) { - STARTUPINFO startup_info = {0}; +bool LaunchProcess(const string16& cmdline, + const LaunchOptions& options) { + STARTUPINFO startup_info = {}; startup_info.cb = sizeof(startup_info); + if (options.empty_desktop_name) + startup_info.lpDesktop = L""; startup_info.dwFlags = STARTF_USESHOWWINDOW; - startup_info.wShowWindow = start_hidden ? SW_HIDE : SW_SHOW; + startup_info.wShowWindow = options.start_hidden ? SW_HIDE : SW_SHOW; PROCESS_INFORMATION process_info; - if (!CreateProcess(NULL, - const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL, - inherit_handles, 0, NULL, NULL, - &startup_info, &process_info)) - return false; - // Handles must be closed or they will leak - CloseHandle(process_info.hThread); + if (options.as_user) { + DWORD flags = CREATE_UNICODE_ENVIRONMENT; + void* enviroment_block = NULL; - if (wait) - WaitForSingleObject(process_info.hProcess, INFINITE); + if (!CreateEnvironmentBlock(&enviroment_block, options.as_user, FALSE)) + return false; - // If the caller wants the process handle, we won't close it. - if (process_handle) { - *process_handle = process_info.hProcess; + BOOL launched = + CreateProcessAsUser(options.as_user, NULL, + const_cast<wchar_t*>(cmdline.c_str()), + NULL, NULL, options.inherit_handles, flags, + enviroment_block, NULL, &startup_info, + &process_info); + DestroyEnvironmentBlock(enviroment_block); + if (!launched) + return false; } else { - CloseHandle(process_info.hProcess); - } - return true; -} - -bool LaunchApp(const std::wstring& cmdline, - bool wait, bool start_hidden, ProcessHandle* process_handle) { - return LaunchAppImpl(cmdline, wait, start_hidden, false, process_handle); -} - -bool LaunchAppWithHandleInheritance( - const std::wstring& cmdline, bool wait, bool start_hidden, - ProcessHandle* process_handle) { - return LaunchAppImpl(cmdline, wait, start_hidden, true, process_handle); -} - -bool LaunchAppAsUser(UserTokenHandle token, const std::wstring& cmdline, - bool start_hidden, ProcessHandle* process_handle) { - return LaunchAppAsUser(token, cmdline, start_hidden, process_handle, - false, false); -} - -bool LaunchAppAsUser(UserTokenHandle token, const std::wstring& cmdline, - bool start_hidden, ProcessHandle* process_handle, - bool empty_desktop_name, bool inherit_handles) { - STARTUPINFO startup_info = {0}; - startup_info.cb = sizeof(startup_info); - if (empty_desktop_name) - startup_info.lpDesktop = L""; - PROCESS_INFORMATION process_info; - if (start_hidden) { - startup_info.dwFlags = STARTF_USESHOWWINDOW; - startup_info.wShowWindow = SW_HIDE; + if (!CreateProcess(NULL, + const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL, + options.inherit_handles, 0, NULL, NULL, + &startup_info, &process_info)) { + return false; + } } - DWORD flags = CREATE_UNICODE_ENVIRONMENT; - void* enviroment_block = NULL; - - if (!CreateEnvironmentBlock(&enviroment_block, token, FALSE)) - return false; - - BOOL launched = - CreateProcessAsUser(token, NULL, const_cast<wchar_t*>(cmdline.c_str()), - NULL, NULL, inherit_handles, flags, enviroment_block, - NULL, &startup_info, &process_info); - - DestroyEnvironmentBlock(enviroment_block); - - if (!launched) - return false; + // Handles must be closed or they will leak. CloseHandle(process_info.hThread); - if (process_handle) { - *process_handle = process_info.hProcess; + if (options.wait) + WaitForSingleObject(process_info.hProcess, INFINITE); + + // If the caller wants the process handle, we won't close it. + if (options.process_handle) { + *options.process_handle = process_info.hProcess; } else { CloseHandle(process_info.hProcess); } return true; } -bool LaunchApp(const CommandLine& cl, - bool wait, bool start_hidden, ProcessHandle* process_handle) { - return LaunchAppImpl(cl.command_line_string(), wait, - start_hidden, false, process_handle); +bool LaunchProcess(const CommandLine& cmdline, + const LaunchOptions& options) { + return LaunchProcess(cmdline.command_line_string(), options); } // Attempts to kill the process identified by the given process |