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 : #include "base/process_util.h"
6 :
7 : #include <ctype.h>
8 : #include <dirent.h>
9 : #include <fcntl.h>
10 : #include <unistd.h>
11 : #include <string>
12 : #include <sys/types.h>
13 : #include <sys/wait.h>
14 :
15 : #include "base/debug_util.h"
16 : #include "base/eintr_wrapper.h"
17 : #include "base/file_util.h"
18 : #include "base/logging.h"
19 : #include "base/string_tokenizer.h"
20 : #include "base/string_util.h"
21 :
22 : #ifdef MOZ_MEMORY_ANDROID
23 : #include "jemalloc.h"
24 : #endif
25 :
26 : namespace {
27 :
28 : enum ParsingState {
29 : KEY_NAME,
30 : KEY_VALUE
31 : };
32 :
33 1464 : static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
34 :
35 : } // namespace
36 :
37 : namespace base {
38 :
39 0 : bool LaunchApp(const std::vector<std::string>& argv,
40 : const file_handle_mapping_vector& fds_to_remap,
41 : bool wait, ProcessHandle* process_handle) {
42 0 : return LaunchApp(argv, fds_to_remap, environment_map(),
43 0 : wait, process_handle);
44 : }
45 :
46 0 : bool LaunchApp(const std::vector<std::string>& argv,
47 : const file_handle_mapping_vector& fds_to_remap,
48 : const environment_map& env_vars_to_set,
49 : bool wait, ProcessHandle* process_handle,
50 : ProcessArchitecture arch) {
51 0 : scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);
52 : // Illegal to allocate memory after fork and before execvp
53 0 : InjectiveMultimap fd_shuffle1, fd_shuffle2;
54 0 : fd_shuffle1.reserve(fds_to_remap.size());
55 0 : fd_shuffle2.reserve(fds_to_remap.size());
56 :
57 0 : pid_t pid = fork();
58 0 : if (pid < 0)
59 0 : return false;
60 :
61 0 : if (pid == 0) {
62 0 : for (file_handle_mapping_vector::const_iterator
63 0 : it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) {
64 0 : fd_shuffle1.push_back(InjectionArc(it->first, it->second, false));
65 0 : fd_shuffle2.push_back(InjectionArc(it->first, it->second, false));
66 : }
67 :
68 0 : if (!ShuffleFileDescriptors(&fd_shuffle1))
69 0 : _exit(127);
70 :
71 0 : CloseSuperfluousFds(fd_shuffle2);
72 :
73 0 : for (environment_map::const_iterator it = env_vars_to_set.begin();
74 0 : it != env_vars_to_set.end(); ++it) {
75 0 : if (setenv(it->first.c_str(), it->second.c_str(), 1/*overwrite*/))
76 0 : _exit(127);
77 : }
78 :
79 0 : for (size_t i = 0; i < argv.size(); i++)
80 0 : argv_cstr[i] = const_cast<char*>(argv[i].c_str());
81 0 : argv_cstr[argv.size()] = NULL;
82 0 : execvp(argv_cstr[0], argv_cstr.get());
83 : // if we get here, we're in serious trouble and should complain loudly
84 0 : DLOG(ERROR) << "FAILED TO exec() CHILD PROCESS, path: " << argv_cstr[0];
85 0 : exit(127);
86 : } else {
87 : gProcessLog.print("==> process %d launched child process %d\n",
88 0 : GetCurrentProcId(), pid);
89 0 : if (wait)
90 0 : HANDLE_EINTR(waitpid(pid, 0, 0));
91 :
92 0 : if (process_handle)
93 0 : *process_handle = pid;
94 : }
95 :
96 0 : return true;
97 : }
98 :
99 0 : bool LaunchApp(const CommandLine& cl,
100 : bool wait, bool start_hidden,
101 : ProcessHandle* process_handle) {
102 0 : file_handle_mapping_vector no_files;
103 0 : return LaunchApp(cl.argv(), no_files, wait, process_handle);
104 : }
105 :
106 0 : NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name,
107 : const ProcessFilter* filter)
108 0 : : executable_name_(executable_name), filter_(filter) {
109 0 : procfs_dir_ = opendir("/proc");
110 0 : }
111 :
112 0 : NamedProcessIterator::~NamedProcessIterator() {
113 0 : if (procfs_dir_) {
114 0 : closedir(procfs_dir_);
115 0 : procfs_dir_ = NULL;
116 : }
117 0 : }
118 :
119 0 : const ProcessEntry* NamedProcessIterator::NextProcessEntry() {
120 0 : bool result = false;
121 0 : do {
122 0 : result = CheckForNextProcess();
123 0 : } while (result && !IncludeEntry());
124 :
125 0 : if (result)
126 0 : return &entry_;
127 :
128 0 : return NULL;
129 : }
130 :
131 0 : bool NamedProcessIterator::CheckForNextProcess() {
132 : // TODO(port): skip processes owned by different UID
133 :
134 0 : dirent* slot = 0;
135 : const char* openparen;
136 : const char* closeparen;
137 :
138 : // Arbitrarily guess that there will never be more than 200 non-process
139 : // files in /proc. Hardy has 53.
140 0 : int skipped = 0;
141 0 : const int kSkipLimit = 200;
142 0 : while (skipped < kSkipLimit) {
143 0 : slot = readdir(procfs_dir_);
144 : // all done looking through /proc?
145 0 : if (!slot)
146 0 : return false;
147 :
148 : // If not a process, keep looking for one.
149 0 : bool notprocess = false;
150 : int i;
151 0 : for (i = 0; i < NAME_MAX && slot->d_name[i]; ++i) {
152 0 : if (!isdigit(slot->d_name[i])) {
153 0 : notprocess = true;
154 0 : break;
155 : }
156 : }
157 0 : if (i == NAME_MAX || notprocess) {
158 0 : skipped++;
159 0 : continue;
160 : }
161 :
162 : // Read the process's status.
163 : char buf[NAME_MAX + 12];
164 0 : sprintf(buf, "/proc/%s/stat", slot->d_name);
165 0 : FILE *fp = fopen(buf, "r");
166 0 : if (!fp)
167 0 : return false;
168 0 : const char* result = fgets(buf, sizeof(buf), fp);
169 0 : fclose(fp);
170 0 : if (!result)
171 0 : return false;
172 :
173 : // Parse the status. It is formatted like this:
174 : // %d (%s) %c %d ...
175 : // pid (name) runstate ppid
176 : // To avoid being fooled by names containing a closing paren, scan
177 : // backwards.
178 0 : openparen = strchr(buf, '(');
179 0 : closeparen = strrchr(buf, ')');
180 0 : if (!openparen || !closeparen)
181 0 : return false;
182 0 : char runstate = closeparen[2];
183 :
184 : // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped?
185 : // Allowed values: D R S T Z
186 0 : if (runstate != 'Z')
187 0 : break;
188 :
189 : // Nope, it's a zombie; somebody isn't cleaning up after their children.
190 : // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.)
191 : // There could be a lot of zombies, can't really decrement i here.
192 : }
193 0 : if (skipped >= kSkipLimit) {
194 0 : NOTREACHED();
195 0 : return false;
196 : }
197 :
198 0 : entry_.pid = atoi(slot->d_name);
199 0 : entry_.ppid = atoi(closeparen + 3);
200 :
201 : // TODO(port): read pid's commandline's $0, like killall does. Using the
202 : // short name between openparen and closeparen won't work for long names!
203 0 : int len = closeparen - openparen - 1;
204 0 : if (len > NAME_MAX)
205 0 : len = NAME_MAX;
206 0 : memcpy(entry_.szExeFile, openparen + 1, len);
207 0 : entry_.szExeFile[len] = 0;
208 :
209 0 : return true;
210 : }
211 :
212 0 : bool NamedProcessIterator::IncludeEntry() {
213 : // TODO(port): make this also work for non-ASCII filenames
214 0 : if (WideToASCII(executable_name_) != entry_.szExeFile)
215 0 : return false;
216 0 : if (!filter_)
217 0 : return true;
218 0 : return filter_->Includes(entry_.pid, entry_.ppid);
219 : }
220 :
221 : // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING
222 : // in your kernel configuration.
223 0 : bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
224 0 : std::string proc_io_contents;
225 0 : if (!file_util::ReadFileToString(L"/proc/self/io", &proc_io_contents))
226 0 : return false;
227 :
228 0 : (*io_counters).OtherOperationCount = 0;
229 0 : (*io_counters).OtherTransferCount = 0;
230 :
231 0 : StringTokenizer tokenizer(proc_io_contents, ": \n");
232 0 : ParsingState state = KEY_NAME;
233 0 : std::string last_key_name;
234 0 : while (tokenizer.GetNext()) {
235 0 : switch (state) {
236 : case KEY_NAME:
237 0 : last_key_name = tokenizer.token();
238 0 : state = KEY_VALUE;
239 0 : break;
240 : case KEY_VALUE:
241 0 : DCHECK(!last_key_name.empty());
242 0 : if (last_key_name == "syscr") {
243 0 : (*io_counters).ReadOperationCount = StringToInt64(tokenizer.token());
244 0 : } else if (last_key_name == "syscw") {
245 0 : (*io_counters).WriteOperationCount = StringToInt64(tokenizer.token());
246 0 : } else if (last_key_name == "rchar") {
247 0 : (*io_counters).ReadTransferCount = StringToInt64(tokenizer.token());
248 0 : } else if (last_key_name == "wchar") {
249 0 : (*io_counters).WriteTransferCount = StringToInt64(tokenizer.token());
250 : }
251 0 : state = KEY_NAME;
252 0 : break;
253 : }
254 : }
255 0 : return true;
256 : }
257 :
258 4392 : } // namespace base
|