|
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. |
|
4 |
|
5 // derived from process_util_linux.cc and process_util_mac.cc |
|
6 |
|
7 #include "base/process_util.h" |
|
8 |
|
9 #include <sys/param.h> |
|
10 #include <sys/sysctl.h> |
|
11 #include <sys/wait.h> |
|
12 #if defined(OS_DRAGONFLY) || defined(OS_FREEBSD) |
|
13 #include <sys/user.h> |
|
14 #endif |
|
15 |
|
16 #include <ctype.h> |
|
17 #include <fcntl.h> |
|
18 #include <kvm.h> |
|
19 #include <unistd.h> |
|
20 |
|
21 #include <string> |
|
22 |
|
23 #include "base/debug_util.h" |
|
24 #include "base/eintr_wrapper.h" |
|
25 #include "base/file_util.h" |
|
26 #include "base/logging.h" |
|
27 #include "base/string_tokenizer.h" |
|
28 #include "base/string_util.h" |
|
29 |
|
30 #if defined(_POSIX_SPAWN) && _POSIX_SPAWN > 0 |
|
31 #define HAVE_POSIX_SPAWN 1 |
|
32 #endif |
|
33 |
|
34 /* |
|
35 * On platforms that are not gonk based, we fall back to an arbitrary |
|
36 * UID. This is generally the UID for user `nobody', albeit it is not |
|
37 * always the case. |
|
38 */ |
|
39 |
|
40 #if defined(OS_NETBSD) || defined(OS_OPENBSD) |
|
41 # define CHILD_UNPRIVILEGED_UID 32767 |
|
42 # define CHILD_UNPRIVILEGED_GID 32767 |
|
43 #else |
|
44 # define CHILD_UNPRIVILEGED_UID 65534 |
|
45 # define CHILD_UNPRIVILEGED_GID 65534 |
|
46 #endif |
|
47 |
|
48 #ifndef __dso_public |
|
49 # ifdef __exported |
|
50 # define __dso_public __exported |
|
51 # else |
|
52 # define __dso_public __attribute__((__visibility__("default"))) |
|
53 # endif |
|
54 #endif |
|
55 |
|
56 #ifdef HAVE_POSIX_SPAWN |
|
57 #include <spawn.h> |
|
58 extern "C" char **environ __dso_public; |
|
59 #endif |
|
60 |
|
61 namespace { |
|
62 |
|
63 enum ParsingState { |
|
64 KEY_NAME, |
|
65 KEY_VALUE |
|
66 }; |
|
67 |
|
68 static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG"); |
|
69 |
|
70 } // namespace |
|
71 |
|
72 namespace base { |
|
73 |
|
74 #ifdef HAVE_POSIX_SPAWN |
|
75 |
|
76 void FreeEnvVarsArray(char* array[], int length) |
|
77 { |
|
78 for (int i = 0; i < length; i++) { |
|
79 free(array[i]); |
|
80 } |
|
81 delete[] array; |
|
82 } |
|
83 |
|
84 bool LaunchApp(const std::vector<std::string>& argv, |
|
85 const file_handle_mapping_vector& fds_to_remap, |
|
86 bool wait, ProcessHandle* process_handle) { |
|
87 return LaunchApp(argv, fds_to_remap, environment_map(), |
|
88 wait, process_handle); |
|
89 } |
|
90 |
|
91 bool LaunchApp(const std::vector<std::string>& argv, |
|
92 const file_handle_mapping_vector& fds_to_remap, |
|
93 const environment_map& env_vars_to_set, |
|
94 bool wait, ProcessHandle* process_handle, |
|
95 ProcessArchitecture arch) { |
|
96 return LaunchApp(argv, fds_to_remap, env_vars_to_set, |
|
97 PRIVILEGES_INHERIT, |
|
98 wait, process_handle); |
|
99 } |
|
100 |
|
101 bool LaunchApp(const std::vector<std::string>& argv, |
|
102 const file_handle_mapping_vector& fds_to_remap, |
|
103 const environment_map& env_vars_to_set, |
|
104 ChildPrivileges privs, |
|
105 bool wait, ProcessHandle* process_handle, |
|
106 ProcessArchitecture arch) { |
|
107 bool retval = true; |
|
108 |
|
109 char* argv_copy[argv.size() + 1]; |
|
110 for (size_t i = 0; i < argv.size(); i++) { |
|
111 argv_copy[i] = const_cast<char*>(argv[i].c_str()); |
|
112 } |
|
113 argv_copy[argv.size()] = NULL; |
|
114 |
|
115 // Make sure we don't leak any FDs to the child process by marking all FDs |
|
116 // as close-on-exec. |
|
117 SetAllFDsToCloseOnExec(); |
|
118 |
|
119 // Copy environment to a new char array and add the variables |
|
120 // in env_vars_to_set. |
|
121 // Existing variables are overwritten by env_vars_to_set. |
|
122 int pos = 0; |
|
123 environment_map combined_env_vars = env_vars_to_set; |
|
124 while(environ[pos] != NULL) { |
|
125 std::string varString = environ[pos]; |
|
126 std::string varName = varString.substr(0, varString.find_first_of('=')); |
|
127 std::string varValue = varString.substr(varString.find_first_of('=') + 1); |
|
128 if (combined_env_vars.find(varName) == combined_env_vars.end()) { |
|
129 combined_env_vars[varName] = varValue; |
|
130 } |
|
131 pos++; |
|
132 } |
|
133 int varsLen = combined_env_vars.size() + 1; |
|
134 |
|
135 char** vars = new char*[varsLen]; |
|
136 int i = 0; |
|
137 for (environment_map::const_iterator it = combined_env_vars.begin(); |
|
138 it != combined_env_vars.end(); ++it) { |
|
139 std::string entry(it->first); |
|
140 entry += "="; |
|
141 entry += it->second; |
|
142 vars[i] = strdup(entry.c_str()); |
|
143 i++; |
|
144 } |
|
145 vars[i] = NULL; |
|
146 |
|
147 posix_spawn_file_actions_t file_actions; |
|
148 if (posix_spawn_file_actions_init(&file_actions) != 0) { |
|
149 FreeEnvVarsArray(vars, varsLen); |
|
150 return false; |
|
151 } |
|
152 |
|
153 // Turn fds_to_remap array into a set of dup2 calls. |
|
154 for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin(); |
|
155 it != fds_to_remap.end(); |
|
156 ++it) { |
|
157 int src_fd = it->first; |
|
158 int dest_fd = it->second; |
|
159 |
|
160 if (src_fd == dest_fd) { |
|
161 int flags = fcntl(src_fd, F_GETFD); |
|
162 if (flags != -1) { |
|
163 fcntl(src_fd, F_SETFD, flags & ~FD_CLOEXEC); |
|
164 } |
|
165 } else { |
|
166 if (posix_spawn_file_actions_adddup2(&file_actions, src_fd, dest_fd) != 0) { |
|
167 posix_spawn_file_actions_destroy(&file_actions); |
|
168 FreeEnvVarsArray(vars, varsLen); |
|
169 return false; |
|
170 } |
|
171 } |
|
172 } |
|
173 |
|
174 pid_t pid = 0; |
|
175 int spawn_succeeded = (posix_spawnp(&pid, |
|
176 argv_copy[0], |
|
177 &file_actions, |
|
178 NULL, |
|
179 argv_copy, |
|
180 vars) == 0); |
|
181 |
|
182 FreeEnvVarsArray(vars, varsLen); |
|
183 |
|
184 posix_spawn_file_actions_destroy(&file_actions); |
|
185 |
|
186 bool process_handle_valid = pid > 0; |
|
187 if (!spawn_succeeded || !process_handle_valid) { |
|
188 retval = false; |
|
189 } else { |
|
190 if (wait) |
|
191 HANDLE_EINTR(waitpid(pid, 0, 0)); |
|
192 |
|
193 if (process_handle) |
|
194 *process_handle = pid; |
|
195 } |
|
196 |
|
197 return retval; |
|
198 } |
|
199 |
|
200 bool LaunchApp(const CommandLine& cl, |
|
201 bool wait, bool start_hidden, ProcessHandle* process_handle) { |
|
202 // TODO(playmobil): Do we need to respect the start_hidden flag? |
|
203 file_handle_mapping_vector no_files; |
|
204 return LaunchApp(cl.argv(), no_files, wait, process_handle); |
|
205 } |
|
206 |
|
207 void SetCurrentProcessPrivileges(ChildPrivileges privs) { |
|
208 |
|
209 } |
|
210 |
|
211 #else // no posix_spawn, use fork/exec |
|
212 |
|
213 bool LaunchApp(const std::vector<std::string>& argv, |
|
214 const file_handle_mapping_vector& fds_to_remap, |
|
215 bool wait, ProcessHandle* process_handle) { |
|
216 return LaunchApp(argv, fds_to_remap, environment_map(), |
|
217 wait, process_handle); |
|
218 } |
|
219 |
|
220 bool LaunchApp(const std::vector<std::string>& argv, |
|
221 const file_handle_mapping_vector& fds_to_remap, |
|
222 const environment_map& env_vars_to_set, |
|
223 bool wait, ProcessHandle* process_handle, |
|
224 ProcessArchitecture arch) { |
|
225 return LaunchApp(argv, fds_to_remap, env_vars_to_set, |
|
226 PRIVILEGES_INHERIT, |
|
227 wait, process_handle); |
|
228 } |
|
229 |
|
230 bool LaunchApp(const std::vector<std::string>& argv, |
|
231 const file_handle_mapping_vector& fds_to_remap, |
|
232 const environment_map& env_vars_to_set, |
|
233 ChildPrivileges privs, |
|
234 bool wait, ProcessHandle* process_handle, |
|
235 ProcessArchitecture arch) { |
|
236 scoped_array<char*> argv_cstr(new char*[argv.size() + 1]); |
|
237 // Illegal to allocate memory after fork and before execvp |
|
238 InjectiveMultimap fd_shuffle1, fd_shuffle2; |
|
239 fd_shuffle1.reserve(fds_to_remap.size()); |
|
240 fd_shuffle2.reserve(fds_to_remap.size()); |
|
241 |
|
242 pid_t pid = fork(); |
|
243 if (pid < 0) |
|
244 return false; |
|
245 |
|
246 if (pid == 0) { |
|
247 for (file_handle_mapping_vector::const_iterator |
|
248 it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) { |
|
249 fd_shuffle1.push_back(InjectionArc(it->first, it->second, false)); |
|
250 fd_shuffle2.push_back(InjectionArc(it->first, it->second, false)); |
|
251 } |
|
252 |
|
253 if (!ShuffleFileDescriptors(&fd_shuffle1)) |
|
254 _exit(127); |
|
255 |
|
256 CloseSuperfluousFds(fd_shuffle2); |
|
257 |
|
258 for (size_t i = 0; i < argv.size(); i++) |
|
259 argv_cstr[i] = const_cast<char*>(argv[i].c_str()); |
|
260 argv_cstr[argv.size()] = NULL; |
|
261 |
|
262 SetCurrentProcessPrivileges(privs); |
|
263 |
|
264 for (environment_map::const_iterator it = env_vars_to_set.begin(); |
|
265 it != env_vars_to_set.end(); ++it) { |
|
266 if (setenv(it->first.c_str(), it->second.c_str(), 1/*overwrite*/)) |
|
267 _exit(127); |
|
268 } |
|
269 execv(argv_cstr[0], argv_cstr.get()); |
|
270 // if we get here, we're in serious trouble and should complain loudly |
|
271 DLOG(ERROR) << "FAILED TO exec() CHILD PROCESS, path: " << argv_cstr[0]; |
|
272 _exit(127); |
|
273 } else { |
|
274 gProcessLog.print("==> process %d launched child process %d\n", |
|
275 GetCurrentProcId(), pid); |
|
276 if (wait) |
|
277 HANDLE_EINTR(waitpid(pid, 0, 0)); |
|
278 |
|
279 if (process_handle) |
|
280 *process_handle = pid; |
|
281 } |
|
282 |
|
283 return true; |
|
284 } |
|
285 |
|
286 bool LaunchApp(const CommandLine& cl, |
|
287 bool wait, bool start_hidden, |
|
288 ProcessHandle* process_handle) { |
|
289 file_handle_mapping_vector no_files; |
|
290 return LaunchApp(cl.argv(), no_files, wait, process_handle); |
|
291 } |
|
292 |
|
293 void SetCurrentProcessPrivileges(ChildPrivileges privs) { |
|
294 if (privs == PRIVILEGES_INHERIT) { |
|
295 return; |
|
296 } |
|
297 |
|
298 gid_t gid = CHILD_UNPRIVILEGED_GID; |
|
299 uid_t uid = CHILD_UNPRIVILEGED_UID; |
|
300 if (setgid(gid) != 0) { |
|
301 DLOG(ERROR) << "FAILED TO setgid() CHILD PROCESS"; |
|
302 _exit(127); |
|
303 } |
|
304 if (setuid(uid) != 0) { |
|
305 DLOG(ERROR) << "FAILED TO setuid() CHILD PROCESS"; |
|
306 _exit(127); |
|
307 } |
|
308 if (chdir("/") != 0) |
|
309 gProcessLog.print("==> could not chdir()\n"); |
|
310 } |
|
311 |
|
312 #endif |
|
313 |
|
314 NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name, |
|
315 const ProcessFilter* filter) |
|
316 { |
|
317 int numEntries; |
|
318 kvm_t *kvm; |
|
319 std::string exe(WideToASCII(executable_name)); |
|
320 |
|
321 #if defined(OS_DRAGONFLY) || defined(OS_FREEBSD) |
|
322 kvm = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL); |
|
323 struct kinfo_proc* procs = kvm_getprocs(kvm, KERN_PROC_UID, getuid(), &numEntries); |
|
324 if (procs != NULL && numEntries > 0) { |
|
325 for (int i = 0; i < numEntries; i++) { |
|
326 # if defined(OS_DRAGONFLY) |
|
327 if (exe != procs[i].kp_comm) continue; |
|
328 if (filter && !filter->Includes(procs[i].kp_pid, procs[i].kp_ppid)) continue; |
|
329 ProcessEntry e; |
|
330 e.pid = procs[i].kp_pid; |
|
331 e.ppid = procs[i].kp_ppid; |
|
332 strlcpy(e.szExeFile, procs[i].kp_comm, sizeof e.szExeFile); |
|
333 content.push_back(e); |
|
334 # elif defined(OS_FREEBSD) |
|
335 if (exe != procs[i].ki_comm) continue; |
|
336 if (filter && !filter->Includes(procs[i].ki_pid, procs[i].ki_ppid)) continue; |
|
337 ProcessEntry e; |
|
338 e.pid = procs[i].ki_pid; |
|
339 e.ppid = procs[i].ki_ppid; |
|
340 strlcpy(e.szExeFile, procs[i].ki_comm, sizeof e.szExeFile); |
|
341 content.push_back(e); |
|
342 # endif |
|
343 #else |
|
344 kvm = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, NULL); |
|
345 #if defined(OS_OPENBSD) |
|
346 struct kinfo_proc* procs = kvm_getprocs(kvm, KERN_PROC_UID, getuid(), sizeof(struct kinfo_proc), &numEntries); |
|
347 #else |
|
348 struct kinfo_proc2* procs = kvm_getproc2(kvm, KERN_PROC_UID, getuid(), sizeof(struct kinfo_proc2), &numEntries); |
|
349 #endif |
|
350 if (procs != NULL && numEntries > 0) { |
|
351 for (int i = 0; i < numEntries; i++) { |
|
352 if (exe != procs[i].p_comm) continue; |
|
353 if (filter && !filter->Includes(procs[i].p_pid, procs[i].p_ppid)) continue; |
|
354 ProcessEntry e; |
|
355 e.pid = procs[i].p_pid; |
|
356 e.ppid = procs[i].p_ppid; |
|
357 strlcpy(e.szExeFile, procs[i].p_comm, sizeof e.szExeFile); |
|
358 content.push_back(e); |
|
359 #endif |
|
360 } |
|
361 } |
|
362 nextEntry = 0; |
|
363 kvm_close(kvm); |
|
364 } |
|
365 |
|
366 NamedProcessIterator::~NamedProcessIterator() { |
|
367 } |
|
368 |
|
369 const ProcessEntry* NamedProcessIterator::NextProcessEntry() { |
|
370 if (nextEntry >= content.size()) return NULL; |
|
371 return &content[nextEntry++]; |
|
372 } |
|
373 |
|
374 } // namespace base |