1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/chromium/src/base/process_util_mac.mm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,329 @@ 1.4 +// Copyright (c) 2008 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 + 1.9 +#include "base/process_util.h" 1.10 + 1.11 +#import <Cocoa/Cocoa.h> 1.12 +#include <crt_externs.h> 1.13 +#include <spawn.h> 1.14 +#include <sys/sysctl.h> 1.15 +#include <sys/types.h> 1.16 +#include <sys/wait.h> 1.17 + 1.18 +#include <string> 1.19 + 1.20 +#include "base/eintr_wrapper.h" 1.21 +#include "base/logging.h" 1.22 +#include "base/rand_util.h" 1.23 +#include "base/string_util.h" 1.24 +#include "base/time.h" 1.25 + 1.26 +namespace base { 1.27 + 1.28 +void FreeEnvVarsArray(char* array[], int length) 1.29 +{ 1.30 + for (int i = 0; i < length; i++) { 1.31 + free(array[i]); 1.32 + } 1.33 + delete[] array; 1.34 +} 1.35 + 1.36 +bool LaunchApp(const std::vector<std::string>& argv, 1.37 + const file_handle_mapping_vector& fds_to_remap, 1.38 + bool wait, ProcessHandle* process_handle) { 1.39 + return LaunchApp(argv, fds_to_remap, environment_map(), 1.40 + wait, process_handle); 1.41 +} 1.42 + 1.43 +bool LaunchApp(const std::vector<std::string>& argv, 1.44 + const file_handle_mapping_vector& fds_to_remap, 1.45 + const environment_map& env_vars_to_set, 1.46 + bool wait, ProcessHandle* process_handle, 1.47 + ProcessArchitecture arch) { 1.48 + return LaunchApp(argv, fds_to_remap, env_vars_to_set, 1.49 + PRIVILEGES_INHERIT, 1.50 + wait, process_handle); 1.51 +} 1.52 + 1.53 +bool LaunchApp(const std::vector<std::string>& argv, 1.54 + const file_handle_mapping_vector& fds_to_remap, 1.55 + const environment_map& env_vars_to_set, 1.56 + ChildPrivileges privs, 1.57 + bool wait, ProcessHandle* process_handle, 1.58 + ProcessArchitecture arch) { 1.59 + bool retval = true; 1.60 + 1.61 + char* argv_copy[argv.size() + 1]; 1.62 + for (size_t i = 0; i < argv.size(); i++) { 1.63 + argv_copy[i] = const_cast<char*>(argv[i].c_str()); 1.64 + } 1.65 + argv_copy[argv.size()] = NULL; 1.66 + 1.67 + // Make sure we don't leak any FDs to the child process by marking all FDs 1.68 + // as close-on-exec. 1.69 + SetAllFDsToCloseOnExec(); 1.70 + 1.71 + // Copy _NSGetEnviron() to a new char array and add the variables 1.72 + // in env_vars_to_set. 1.73 + // Existing variables are overwritten by env_vars_to_set. 1.74 + int pos = 0; 1.75 + environment_map combined_env_vars = env_vars_to_set; 1.76 + while((*_NSGetEnviron())[pos] != NULL) { 1.77 + std::string varString = (*_NSGetEnviron())[pos]; 1.78 + std::string varName = varString.substr(0, varString.find_first_of('=')); 1.79 + std::string varValue = varString.substr(varString.find_first_of('=') + 1); 1.80 + if (combined_env_vars.find(varName) == combined_env_vars.end()) { 1.81 + combined_env_vars[varName] = varValue; 1.82 + } 1.83 + pos++; 1.84 + } 1.85 + int varsLen = combined_env_vars.size() + 1; 1.86 + 1.87 + char** vars = new char*[varsLen]; 1.88 + int i = 0; 1.89 + for (environment_map::const_iterator it = combined_env_vars.begin(); 1.90 + it != combined_env_vars.end(); ++it) { 1.91 + std::string entry(it->first); 1.92 + entry += "="; 1.93 + entry += it->second; 1.94 + vars[i] = strdup(entry.c_str()); 1.95 + i++; 1.96 + } 1.97 + vars[i] = NULL; 1.98 + 1.99 + posix_spawn_file_actions_t file_actions; 1.100 + if (posix_spawn_file_actions_init(&file_actions) != 0) { 1.101 + FreeEnvVarsArray(vars, varsLen); 1.102 + return false; 1.103 + } 1.104 + 1.105 + // Turn fds_to_remap array into a set of dup2 calls. 1.106 + for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin(); 1.107 + it != fds_to_remap.end(); 1.108 + ++it) { 1.109 + int src_fd = it->first; 1.110 + int dest_fd = it->second; 1.111 + 1.112 + if (src_fd == dest_fd) { 1.113 + int flags = fcntl(src_fd, F_GETFD); 1.114 + if (flags != -1) { 1.115 + fcntl(src_fd, F_SETFD, flags & ~FD_CLOEXEC); 1.116 + } 1.117 + } else { 1.118 + if (posix_spawn_file_actions_adddup2(&file_actions, src_fd, dest_fd) != 0) { 1.119 + posix_spawn_file_actions_destroy(&file_actions); 1.120 + FreeEnvVarsArray(vars, varsLen); 1.121 + return false; 1.122 + } 1.123 + } 1.124 + } 1.125 + 1.126 + // Set up the CPU preference array. 1.127 + cpu_type_t cpu_types[1]; 1.128 + switch (arch) { 1.129 + case PROCESS_ARCH_I386: 1.130 + cpu_types[0] = CPU_TYPE_X86; 1.131 + break; 1.132 + case PROCESS_ARCH_X86_64: 1.133 + cpu_types[0] = CPU_TYPE_X86_64; 1.134 + break; 1.135 + case PROCESS_ARCH_PPC: 1.136 + cpu_types[0] = CPU_TYPE_POWERPC; 1.137 + default: 1.138 + cpu_types[0] = CPU_TYPE_ANY; 1.139 + break; 1.140 + } 1.141 + 1.142 + // Initialize spawn attributes. 1.143 + posix_spawnattr_t spawnattr; 1.144 + if (posix_spawnattr_init(&spawnattr) != 0) { 1.145 + FreeEnvVarsArray(vars, varsLen); 1.146 + return false; 1.147 + } 1.148 + 1.149 + // Set spawn attributes. 1.150 + size_t attr_count = 1; 1.151 + size_t attr_ocount = 0; 1.152 + if (posix_spawnattr_setbinpref_np(&spawnattr, attr_count, cpu_types, &attr_ocount) != 0 || 1.153 + attr_ocount != attr_count) { 1.154 + FreeEnvVarsArray(vars, varsLen); 1.155 + posix_spawnattr_destroy(&spawnattr); 1.156 + return false; 1.157 + } 1.158 + 1.159 + int pid = 0; 1.160 + int spawn_succeeded = (posix_spawnp(&pid, 1.161 + argv_copy[0], 1.162 + &file_actions, 1.163 + &spawnattr, 1.164 + argv_copy, 1.165 + vars) == 0); 1.166 + 1.167 + FreeEnvVarsArray(vars, varsLen); 1.168 + 1.169 + posix_spawn_file_actions_destroy(&file_actions); 1.170 + 1.171 + posix_spawnattr_destroy(&spawnattr); 1.172 + 1.173 + bool process_handle_valid = pid > 0; 1.174 + if (!spawn_succeeded || !process_handle_valid) { 1.175 + retval = false; 1.176 + } else { 1.177 + if (wait) 1.178 + HANDLE_EINTR(waitpid(pid, 0, 0)); 1.179 + 1.180 + if (process_handle) 1.181 + *process_handle = pid; 1.182 + } 1.183 + 1.184 + return retval; 1.185 +} 1.186 + 1.187 +bool LaunchApp(const CommandLine& cl, 1.188 + bool wait, bool start_hidden, ProcessHandle* process_handle) { 1.189 + // TODO(playmobil): Do we need to respect the start_hidden flag? 1.190 + file_handle_mapping_vector no_files; 1.191 + return LaunchApp(cl.argv(), no_files, wait, process_handle); 1.192 +} 1.193 + 1.194 +void SetCurrentProcessPrivileges(ChildPrivileges privs) { 1.195 + 1.196 +} 1.197 + 1.198 +NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name, 1.199 + const ProcessFilter* filter) 1.200 + : executable_name_(executable_name), 1.201 + index_of_kinfo_proc_(0), 1.202 + filter_(filter) { 1.203 + // Get a snapshot of all of my processes (yes, as we loop it can go stale, but 1.204 + // but trying to find where we were in a constantly changing list is basically 1.205 + // impossible. 1.206 + 1.207 + int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, int(geteuid()) }; 1.208 + 1.209 + // Since more processes could start between when we get the size and when 1.210 + // we get the list, we do a loop to keep trying until we get it. 1.211 + bool done = false; 1.212 + int try_num = 1; 1.213 + const int max_tries = 10; 1.214 + do { 1.215 + // Get the size of the buffer 1.216 + size_t len = 0; 1.217 + if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) { 1.218 + CHROMIUM_LOG(ERROR) << "failed to get the size needed for the process list"; 1.219 + kinfo_procs_.resize(0); 1.220 + done = true; 1.221 + } else { 1.222 + size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); 1.223 + // Leave some spare room for process table growth (more could show up 1.224 + // between when we check and now) 1.225 + num_of_kinfo_proc += 4; 1.226 + kinfo_procs_.resize(num_of_kinfo_proc); 1.227 + len = num_of_kinfo_proc * sizeof(struct kinfo_proc); 1.228 + // Load the list of processes 1.229 + if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) { 1.230 + // If we get a mem error, it just means we need a bigger buffer, so 1.231 + // loop around again. Anything else is a real error and give up. 1.232 + if (errno != ENOMEM) { 1.233 + CHROMIUM_LOG(ERROR) << "failed to get the process list"; 1.234 + kinfo_procs_.resize(0); 1.235 + done = true; 1.236 + } 1.237 + } else { 1.238 + // Got the list, just make sure we're sized exactly right 1.239 + size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); 1.240 + kinfo_procs_.resize(num_of_kinfo_proc); 1.241 + done = true; 1.242 + } 1.243 + } 1.244 + } while (!done && (try_num++ < max_tries)); 1.245 + 1.246 + if (!done) { 1.247 + CHROMIUM_LOG(ERROR) << "failed to collect the process list in a few tries"; 1.248 + kinfo_procs_.resize(0); 1.249 + } 1.250 +} 1.251 + 1.252 +NamedProcessIterator::~NamedProcessIterator() { 1.253 +} 1.254 + 1.255 +const ProcessEntry* NamedProcessIterator::NextProcessEntry() { 1.256 + bool result = false; 1.257 + do { 1.258 + result = CheckForNextProcess(); 1.259 + } while (result && !IncludeEntry()); 1.260 + 1.261 + if (result) { 1.262 + return &entry_; 1.263 + } 1.264 + 1.265 + return NULL; 1.266 +} 1.267 + 1.268 +bool NamedProcessIterator::CheckForNextProcess() { 1.269 + std::string executable_name_utf8(WideToUTF8(executable_name_)); 1.270 + 1.271 + std::string data; 1.272 + std::string exec_name; 1.273 + 1.274 + for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) { 1.275 + kinfo_proc* kinfo = &kinfo_procs_[index_of_kinfo_proc_]; 1.276 + 1.277 + // Skip processes just awaiting collection 1.278 + if ((kinfo->kp_proc.p_pid > 0) && (kinfo->kp_proc.p_stat == SZOMB)) 1.279 + continue; 1.280 + 1.281 + int mib[] = { CTL_KERN, KERN_PROCARGS, kinfo->kp_proc.p_pid }; 1.282 + 1.283 + // Found out what size buffer we need 1.284 + size_t data_len = 0; 1.285 + if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) { 1.286 + CHROMIUM_LOG(ERROR) << "failed to figure out the buffer size for a commandline"; 1.287 + continue; 1.288 + } 1.289 + 1.290 + data.resize(data_len); 1.291 + if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) { 1.292 + CHROMIUM_LOG(ERROR) << "failed to fetch a commandline"; 1.293 + continue; 1.294 + } 1.295 + 1.296 + // Data starts w/ the full path null termed, so we have to extract just the 1.297 + // executable name from the path. 1.298 + 1.299 + size_t exec_name_end = data.find('\0'); 1.300 + if (exec_name_end == std::string::npos) { 1.301 + CHROMIUM_LOG(ERROR) << "command line data didn't match expected format"; 1.302 + continue; 1.303 + } 1.304 + size_t last_slash = data.rfind('/', exec_name_end); 1.305 + if (last_slash == std::string::npos) 1.306 + exec_name = data.substr(0, exec_name_end); 1.307 + else 1.308 + exec_name = data.substr(last_slash + 1, exec_name_end - last_slash - 1); 1.309 + 1.310 + // Check the name 1.311 + if (executable_name_utf8 == exec_name) { 1.312 + entry_.pid = kinfo->kp_proc.p_pid; 1.313 + entry_.ppid = kinfo->kp_eproc.e_ppid; 1.314 + base::strlcpy(entry_.szExeFile, exec_name.c_str(), 1.315 + sizeof(entry_.szExeFile)); 1.316 + // Start w/ the next entry next time through 1.317 + ++index_of_kinfo_proc_; 1.318 + // Done 1.319 + return true; 1.320 + } 1.321 + } 1.322 + return false; 1.323 +} 1.324 + 1.325 +bool NamedProcessIterator::IncludeEntry() { 1.326 + // Don't need to check the name, we did that w/in CheckForNextProcess. 1.327 + if (!filter_) 1.328 + return true; 1.329 + return filter_->Includes(entry_.pid, entry_.ppid); 1.330 +} 1.331 + 1.332 +} // namespace base