Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 // Copyright (c) 2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 #include "base/process_util.h"
8 #import <Cocoa/Cocoa.h>
9 #include <crt_externs.h>
10 #include <spawn.h>
11 #include <sys/sysctl.h>
12 #include <sys/types.h>
13 #include <sys/wait.h>
15 #include <string>
17 #include "base/eintr_wrapper.h"
18 #include "base/logging.h"
19 #include "base/rand_util.h"
20 #include "base/string_util.h"
21 #include "base/time.h"
23 namespace base {
25 void FreeEnvVarsArray(char* array[], int length)
26 {
27 for (int i = 0; i < length; i++) {
28 free(array[i]);
29 }
30 delete[] array;
31 }
33 bool LaunchApp(const std::vector<std::string>& argv,
34 const file_handle_mapping_vector& fds_to_remap,
35 bool wait, ProcessHandle* process_handle) {
36 return LaunchApp(argv, fds_to_remap, environment_map(),
37 wait, process_handle);
38 }
40 bool LaunchApp(const std::vector<std::string>& argv,
41 const file_handle_mapping_vector& fds_to_remap,
42 const environment_map& env_vars_to_set,
43 bool wait, ProcessHandle* process_handle,
44 ProcessArchitecture arch) {
45 return LaunchApp(argv, fds_to_remap, env_vars_to_set,
46 PRIVILEGES_INHERIT,
47 wait, process_handle);
48 }
50 bool LaunchApp(const std::vector<std::string>& argv,
51 const file_handle_mapping_vector& fds_to_remap,
52 const environment_map& env_vars_to_set,
53 ChildPrivileges privs,
54 bool wait, ProcessHandle* process_handle,
55 ProcessArchitecture arch) {
56 bool retval = true;
58 char* argv_copy[argv.size() + 1];
59 for (size_t i = 0; i < argv.size(); i++) {
60 argv_copy[i] = const_cast<char*>(argv[i].c_str());
61 }
62 argv_copy[argv.size()] = NULL;
64 // Make sure we don't leak any FDs to the child process by marking all FDs
65 // as close-on-exec.
66 SetAllFDsToCloseOnExec();
68 // Copy _NSGetEnviron() to a new char array and add the variables
69 // in env_vars_to_set.
70 // Existing variables are overwritten by env_vars_to_set.
71 int pos = 0;
72 environment_map combined_env_vars = env_vars_to_set;
73 while((*_NSGetEnviron())[pos] != NULL) {
74 std::string varString = (*_NSGetEnviron())[pos];
75 std::string varName = varString.substr(0, varString.find_first_of('='));
76 std::string varValue = varString.substr(varString.find_first_of('=') + 1);
77 if (combined_env_vars.find(varName) == combined_env_vars.end()) {
78 combined_env_vars[varName] = varValue;
79 }
80 pos++;
81 }
82 int varsLen = combined_env_vars.size() + 1;
84 char** vars = new char*[varsLen];
85 int i = 0;
86 for (environment_map::const_iterator it = combined_env_vars.begin();
87 it != combined_env_vars.end(); ++it) {
88 std::string entry(it->first);
89 entry += "=";
90 entry += it->second;
91 vars[i] = strdup(entry.c_str());
92 i++;
93 }
94 vars[i] = NULL;
96 posix_spawn_file_actions_t file_actions;
97 if (posix_spawn_file_actions_init(&file_actions) != 0) {
98 FreeEnvVarsArray(vars, varsLen);
99 return false;
100 }
102 // Turn fds_to_remap array into a set of dup2 calls.
103 for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin();
104 it != fds_to_remap.end();
105 ++it) {
106 int src_fd = it->first;
107 int dest_fd = it->second;
109 if (src_fd == dest_fd) {
110 int flags = fcntl(src_fd, F_GETFD);
111 if (flags != -1) {
112 fcntl(src_fd, F_SETFD, flags & ~FD_CLOEXEC);
113 }
114 } else {
115 if (posix_spawn_file_actions_adddup2(&file_actions, src_fd, dest_fd) != 0) {
116 posix_spawn_file_actions_destroy(&file_actions);
117 FreeEnvVarsArray(vars, varsLen);
118 return false;
119 }
120 }
121 }
123 // Set up the CPU preference array.
124 cpu_type_t cpu_types[1];
125 switch (arch) {
126 case PROCESS_ARCH_I386:
127 cpu_types[0] = CPU_TYPE_X86;
128 break;
129 case PROCESS_ARCH_X86_64:
130 cpu_types[0] = CPU_TYPE_X86_64;
131 break;
132 case PROCESS_ARCH_PPC:
133 cpu_types[0] = CPU_TYPE_POWERPC;
134 default:
135 cpu_types[0] = CPU_TYPE_ANY;
136 break;
137 }
139 // Initialize spawn attributes.
140 posix_spawnattr_t spawnattr;
141 if (posix_spawnattr_init(&spawnattr) != 0) {
142 FreeEnvVarsArray(vars, varsLen);
143 return false;
144 }
146 // Set spawn attributes.
147 size_t attr_count = 1;
148 size_t attr_ocount = 0;
149 if (posix_spawnattr_setbinpref_np(&spawnattr, attr_count, cpu_types, &attr_ocount) != 0 ||
150 attr_ocount != attr_count) {
151 FreeEnvVarsArray(vars, varsLen);
152 posix_spawnattr_destroy(&spawnattr);
153 return false;
154 }
156 int pid = 0;
157 int spawn_succeeded = (posix_spawnp(&pid,
158 argv_copy[0],
159 &file_actions,
160 &spawnattr,
161 argv_copy,
162 vars) == 0);
164 FreeEnvVarsArray(vars, varsLen);
166 posix_spawn_file_actions_destroy(&file_actions);
168 posix_spawnattr_destroy(&spawnattr);
170 bool process_handle_valid = pid > 0;
171 if (!spawn_succeeded || !process_handle_valid) {
172 retval = false;
173 } else {
174 if (wait)
175 HANDLE_EINTR(waitpid(pid, 0, 0));
177 if (process_handle)
178 *process_handle = pid;
179 }
181 return retval;
182 }
184 bool LaunchApp(const CommandLine& cl,
185 bool wait, bool start_hidden, ProcessHandle* process_handle) {
186 // TODO(playmobil): Do we need to respect the start_hidden flag?
187 file_handle_mapping_vector no_files;
188 return LaunchApp(cl.argv(), no_files, wait, process_handle);
189 }
191 void SetCurrentProcessPrivileges(ChildPrivileges privs) {
193 }
195 NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name,
196 const ProcessFilter* filter)
197 : executable_name_(executable_name),
198 index_of_kinfo_proc_(0),
199 filter_(filter) {
200 // Get a snapshot of all of my processes (yes, as we loop it can go stale, but
201 // but trying to find where we were in a constantly changing list is basically
202 // impossible.
204 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, int(geteuid()) };
206 // Since more processes could start between when we get the size and when
207 // we get the list, we do a loop to keep trying until we get it.
208 bool done = false;
209 int try_num = 1;
210 const int max_tries = 10;
211 do {
212 // Get the size of the buffer
213 size_t len = 0;
214 if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) {
215 CHROMIUM_LOG(ERROR) << "failed to get the size needed for the process list";
216 kinfo_procs_.resize(0);
217 done = true;
218 } else {
219 size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
220 // Leave some spare room for process table growth (more could show up
221 // between when we check and now)
222 num_of_kinfo_proc += 4;
223 kinfo_procs_.resize(num_of_kinfo_proc);
224 len = num_of_kinfo_proc * sizeof(struct kinfo_proc);
225 // Load the list of processes
226 if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) {
227 // If we get a mem error, it just means we need a bigger buffer, so
228 // loop around again. Anything else is a real error and give up.
229 if (errno != ENOMEM) {
230 CHROMIUM_LOG(ERROR) << "failed to get the process list";
231 kinfo_procs_.resize(0);
232 done = true;
233 }
234 } else {
235 // Got the list, just make sure we're sized exactly right
236 size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
237 kinfo_procs_.resize(num_of_kinfo_proc);
238 done = true;
239 }
240 }
241 } while (!done && (try_num++ < max_tries));
243 if (!done) {
244 CHROMIUM_LOG(ERROR) << "failed to collect the process list in a few tries";
245 kinfo_procs_.resize(0);
246 }
247 }
249 NamedProcessIterator::~NamedProcessIterator() {
250 }
252 const ProcessEntry* NamedProcessIterator::NextProcessEntry() {
253 bool result = false;
254 do {
255 result = CheckForNextProcess();
256 } while (result && !IncludeEntry());
258 if (result) {
259 return &entry_;
260 }
262 return NULL;
263 }
265 bool NamedProcessIterator::CheckForNextProcess() {
266 std::string executable_name_utf8(WideToUTF8(executable_name_));
268 std::string data;
269 std::string exec_name;
271 for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) {
272 kinfo_proc* kinfo = &kinfo_procs_[index_of_kinfo_proc_];
274 // Skip processes just awaiting collection
275 if ((kinfo->kp_proc.p_pid > 0) && (kinfo->kp_proc.p_stat == SZOMB))
276 continue;
278 int mib[] = { CTL_KERN, KERN_PROCARGS, kinfo->kp_proc.p_pid };
280 // Found out what size buffer we need
281 size_t data_len = 0;
282 if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) {
283 CHROMIUM_LOG(ERROR) << "failed to figure out the buffer size for a commandline";
284 continue;
285 }
287 data.resize(data_len);
288 if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) {
289 CHROMIUM_LOG(ERROR) << "failed to fetch a commandline";
290 continue;
291 }
293 // Data starts w/ the full path null termed, so we have to extract just the
294 // executable name from the path.
296 size_t exec_name_end = data.find('\0');
297 if (exec_name_end == std::string::npos) {
298 CHROMIUM_LOG(ERROR) << "command line data didn't match expected format";
299 continue;
300 }
301 size_t last_slash = data.rfind('/', exec_name_end);
302 if (last_slash == std::string::npos)
303 exec_name = data.substr(0, exec_name_end);
304 else
305 exec_name = data.substr(last_slash + 1, exec_name_end - last_slash - 1);
307 // Check the name
308 if (executable_name_utf8 == exec_name) {
309 entry_.pid = kinfo->kp_proc.p_pid;
310 entry_.ppid = kinfo->kp_eproc.e_ppid;
311 base::strlcpy(entry_.szExeFile, exec_name.c_str(),
312 sizeof(entry_.szExeFile));
313 // Start w/ the next entry next time through
314 ++index_of_kinfo_proc_;
315 // Done
316 return true;
317 }
318 }
319 return false;
320 }
322 bool NamedProcessIterator::IncludeEntry() {
323 // Don't need to check the name, we did that w/in CheckForNextProcess.
324 if (!filter_)
325 return true;
326 return filter_->Includes(entry_.pid, entry_.ppid);
327 }
329 } // namespace base