OLD | NEW |
1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/process_util.h" | 5 #include "base/process_util.h" |
6 | 6 |
| 7 #include <ctype.h> |
| 8 #include <dirent.h> |
7 #include <string> | 9 #include <string> |
8 #include <sys/types.h> | 10 #include <sys/types.h> |
9 #include <sys/wait.h> | 11 #include <sys/wait.h> |
10 | 12 |
11 #include "base/file_util.h" | 13 #include "base/file_util.h" |
12 #include "base/logging.h" | 14 #include "base/logging.h" |
13 #include "base/string_tokenizer.h" | 15 #include "base/string_tokenizer.h" |
14 #include "base/string_util.h" | 16 #include "base/string_util.h" |
| 17 #include "base/time.h" |
15 | 18 |
16 namespace { | 19 namespace { |
17 | 20 |
18 enum ParsingState { | 21 enum ParsingState { |
19 KEY_NAME, | 22 KEY_NAME, |
20 KEY_VALUE | 23 KEY_VALUE |
21 }; | 24 }; |
22 | 25 |
23 } // namespace | 26 } // namespace |
24 | 27 |
25 namespace process_util { | 28 namespace process_util { |
26 | 29 |
27 bool LaunchApp(const std::vector<std::string>& argv, | 30 bool LaunchApp(const std::vector<std::string>& argv, |
28 bool wait, ProcessHandle* process_handle) { | 31 bool wait, ProcessHandle* process_handle) { |
29 bool retval = true; | 32 bool retval = true; |
30 | 33 |
31 char* argv_copy[argv.size() + 1]; | 34 char* argv_copy[argv.size() + 1]; |
32 for (size_t i = 0; i < argv.size(); i++) { | 35 for (size_t i = 0; i < argv.size(); i++) { |
33 argv_copy[i] = new char[argv[i].size() + 1]; | 36 argv_copy[i] = new char[argv[i].size() + 1]; |
34 strcpy(argv_copy[i], argv[i].c_str()); | 37 strcpy(argv_copy[i], argv[i].c_str()); |
35 } | 38 } |
36 argv_copy[argv.size()] = NULL; | 39 argv_copy[argv.size()] = NULL; |
37 | 40 |
38 int pid = vfork(); | 41 int pid = fork(); |
39 if (pid == 0) { | 42 if (pid == 0) { |
40 execv(argv_copy[0], argv_copy); | 43 execv(argv_copy[0], argv_copy); |
41 } else if (pid < 0) { | 44 } else if (pid < 0) { |
42 retval = false; | 45 retval = false; |
43 } else { | 46 } else { |
44 if (wait) | 47 if (wait) |
45 waitpid(pid, 0, 0); | 48 waitpid(pid, 0, 0); |
46 | 49 |
47 if(process_handle) | 50 if(process_handle) |
48 *process_handle = pid; | 51 *process_handle = pid; |
49 } | 52 } |
50 | 53 |
51 for (size_t i = 0; i < argv.size(); i++) | 54 for (size_t i = 0; i < argv.size(); i++) |
52 delete[] argv_copy[i]; | 55 delete[] argv_copy[i]; |
53 | 56 |
54 return retval; | 57 return retval; |
55 } | 58 } |
56 | 59 |
57 bool LaunchApp(const CommandLine& cl, | 60 bool LaunchApp(const CommandLine& cl, |
58 bool wait, bool start_hidden, ProcessHandle* process_handle) { | 61 bool wait, bool start_hidden, ProcessHandle* process_handle) { |
59 return LaunchApp(cl.argv(), wait, process_handle); | 62 return LaunchApp(cl.argv(), wait, process_handle); |
60 } | 63 } |
61 | 64 |
| 65 // Attempts to kill the process identified by the given process |
| 66 // entry structure. Ignores specified exit_code; linux can't force that. |
| 67 // Returns true if this is successful, false otherwise. |
| 68 bool KillProcess(int process_id, int exit_code, bool wait) { |
| 69 bool result = false; |
| 70 |
| 71 int status = kill(process_id, SIGTERM); |
| 72 if (!status && wait) { |
| 73 int tries = 60; |
| 74 // The process may not end immediately due to pending I/O |
| 75 while (tries-- > 0) { |
| 76 int pid = waitpid(process_id, &status, WNOHANG); |
| 77 if (pid == process_id) { |
| 78 result = true; |
| 79 break; |
| 80 } |
| 81 sleep(1); |
| 82 } |
| 83 } |
| 84 if (!result) |
| 85 DLOG(ERROR) << "Unable to terminate process."; |
| 86 return result; |
| 87 } |
| 88 |
| 89 bool DidProcessCrash(ProcessHandle handle) { |
| 90 int status; |
| 91 if (waitpid(handle, &status, WNOHANG)) { |
| 92 // I feel like dancing! |
| 93 return false; |
| 94 } |
| 95 |
| 96 if (WIFSIGNALED(status)) { |
| 97 int signum = WTERMSIG(status); |
| 98 return (signum == SIGSEGV || signum == SIGILL || signum == SIGABRT || signum
== SIGFPE); |
| 99 } |
| 100 |
| 101 if (WIFEXITED(status)) { |
| 102 int exitcode = WEXITSTATUS(status); |
| 103 return (exitcode != 0); |
| 104 } |
| 105 |
| 106 return false; |
| 107 } |
| 108 |
| 109 NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name, |
| 110 const ProcessFilter* filter) |
| 111 : |
| 112 executable_name_(executable_name), |
| 113 filter_(filter) { |
| 114 procfs_dir_ = opendir("/proc"); |
| 115 } |
| 116 |
| 117 NamedProcessIterator::~NamedProcessIterator() { |
| 118 if (procfs_dir_) { |
| 119 closedir(procfs_dir_); |
| 120 procfs_dir_ = 0; |
| 121 } |
| 122 } |
| 123 |
| 124 const ProcessEntry* NamedProcessIterator::NextProcessEntry() { |
| 125 bool result = false; |
| 126 do { |
| 127 result = CheckForNextProcess(); |
| 128 } while (result && !IncludeEntry()); |
| 129 |
| 130 if (result) |
| 131 return &entry_; |
| 132 |
| 133 return NULL; |
| 134 } |
| 135 |
| 136 bool NamedProcessIterator::CheckForNextProcess() { |
| 137 // TODO(port): skip processes owned by different UID |
| 138 |
| 139 dirent* slot = 0; |
| 140 const char* openparen; |
| 141 const char* closeparen; |
| 142 |
| 143 // Arbitrarily guess that there will never be more than 200 non-process files
in /proc. |
| 144 // (Hardy has 53.) |
| 145 int skipped = 0; |
| 146 const int kSkipLimit = 200; |
| 147 while (skipped < kSkipLimit) { |
| 148 slot = readdir(procfs_dir_); |
| 149 // all done looking through /proc? |
| 150 if (!slot) |
| 151 return false; |
| 152 |
| 153 // If not a process, keep looking for one. |
| 154 bool notprocess = false; |
| 155 int i; |
| 156 for (i=0; i < NAME_MAX && slot->d_name[i]; ++i) { |
| 157 if (!isdigit(slot->d_name[i])) { |
| 158 notprocess = true; |
| 159 break; |
| 160 } |
| 161 } |
| 162 if (i == NAME_MAX || notprocess) { |
| 163 skipped++; |
| 164 continue; |
| 165 } |
| 166 |
| 167 // Read the process's status. |
| 168 char buf[NAME_MAX + 12]; |
| 169 sprintf(buf, "/proc/%s/stat", slot->d_name); |
| 170 FILE *fp = fopen(buf, "r"); |
| 171 if (!fp) |
| 172 return false; |
| 173 const char* result = fgets(buf, sizeof(buf), fp); |
| 174 fclose(fp); |
| 175 if (!result) |
| 176 return false; |
| 177 |
| 178 // Parse the status. It is formatted like this: |
| 179 // %d (%s) %c %d ... |
| 180 // pid (name) runstate ppid |
| 181 // To avoid being fooled by names containing a closing paren, scan backwards
. |
| 182 openparen = strchr(buf, '('); |
| 183 closeparen = strrchr(buf, ')'); |
| 184 if (!openparen || !closeparen) |
| 185 return false; |
| 186 char runstate = closeparen[2]; |
| 187 |
| 188 // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped? |
| 189 // Allowed values: D R S T Z |
| 190 if (runstate != 'Z') |
| 191 break; |
| 192 |
| 193 // Nope, it's a zombie; somebody isn't cleaning up after their children. |
| 194 // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.) |
| 195 // There could be a lot of zombies, can't really decrement i here. |
| 196 } |
| 197 if (skipped >= kSkipLimit) { |
| 198 NOTREACHED(); |
| 199 return false; |
| 200 } |
| 201 |
| 202 entry_.pid = atoi(slot->d_name); |
| 203 entry_.ppid = atoi(closeparen+3); |
| 204 |
| 205 // TODO(port): read pid's commandline's $0, like killall does. |
| 206 // Using the short name between openparen and closeparen won't work for long n
ames! |
| 207 int len = closeparen - openparen - 1; |
| 208 if (len > NAME_MAX) |
| 209 len = NAME_MAX; |
| 210 memcpy(entry_.szExeFile, openparen + 1, len); |
| 211 entry_.szExeFile[len] = 0; |
| 212 |
| 213 return true; |
| 214 } |
| 215 |
| 216 bool NamedProcessIterator::IncludeEntry() { |
| 217 // TODO(port): make this also work for non-ASCII filenames |
| 218 bool result = strcmp(WideToASCII(executable_name_).c_str(), entry_.szExeFile)
== 0 && |
| 219 (!filter_ || filter_->Includes(entry_.pid, entry_.ppid)); |
| 220 return result; |
| 221 } |
| 222 |
| 223 int GetProcessCount(const std::wstring& executable_name, |
| 224 const ProcessFilter* filter) { |
| 225 int count = 0; |
| 226 |
| 227 NamedProcessIterator iter(executable_name, filter); |
| 228 while (iter.NextProcessEntry()) |
| 229 ++count; |
| 230 return count; |
| 231 } |
| 232 |
| 233 bool KillProcesses(const std::wstring& executable_name, int exit_code, |
| 234 const ProcessFilter* filter) { |
| 235 bool result = true; |
| 236 const ProcessEntry* entry; |
| 237 |
| 238 NamedProcessIterator iter(executable_name, filter); |
| 239 while ((entry = iter.NextProcessEntry()) != NULL) |
| 240 result = KillProcess((*entry).pid, exit_code, true) && result; |
| 241 |
| 242 return result; |
| 243 } |
| 244 |
| 245 bool WaitForProcessesToExit(const std::wstring& executable_name, |
| 246 int wait_milliseconds, |
| 247 const ProcessFilter* filter) { |
| 248 bool result = false; |
| 249 |
| 250 // TODO(port): This is inefficient, but works if there are multiple procs. |
| 251 // TODO(port): use waitpid to avoid leaving zombies around |
| 252 |
| 253 base::Time end_time = base::Time::Now() + base::TimeDelta::FromMilliseconds(wa
it_milliseconds); |
| 254 do { |
| 255 NamedProcessIterator iter(executable_name, filter); |
| 256 if (!iter.NextProcessEntry()) { |
| 257 result = true; |
| 258 break; |
| 259 } |
| 260 // TODO(port): Improve resolution |
| 261 sleep(1); |
| 262 } while ((base::Time::Now() - end_time) > base::TimeDelta()); |
| 263 |
| 264 return result; |
| 265 } |
| 266 |
62 bool WaitForSingleProcess(ProcessHandle handle, int wait_milliseconds) { | 267 bool WaitForSingleProcess(ProcessHandle handle, int wait_milliseconds) { |
63 int status; | 268 int status; |
64 waitpid(handle, &status, 0); | 269 waitpid(handle, &status, 0); |
65 return WIFEXITED(status); | 270 return WIFEXITED(status); |
66 } | 271 } |
67 | 272 |
| 273 bool CleanupProcesses(const std::wstring& executable_name, |
| 274 int wait_milliseconds, |
| 275 int exit_code, |
| 276 const ProcessFilter* filter) { |
| 277 bool exited_cleanly = |
| 278 process_util::WaitForProcessesToExit(executable_name, wait_milliseconds, |
| 279 filter); |
| 280 if (!exited_cleanly) |
| 281 process_util::KillProcesses(executable_name, exit_code, filter); |
| 282 return exited_cleanly; |
| 283 } |
| 284 |
68 /////////////////////////////////////////////////////////////////////////////// | 285 /////////////////////////////////////////////////////////////////////////////// |
69 //// ProcessMetrics | 286 //// ProcessMetrics |
70 | 287 |
71 // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING | 288 // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING |
72 // in your kernel configuration. | 289 // in your kernel configuration. |
73 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) { | 290 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) { |
74 std::string proc_io_contents; | 291 std::string proc_io_contents; |
75 if (!file_util::ReadFileToString(L"/proc/self/io", &proc_io_contents)) | 292 if (!file_util::ReadFileToString(L"/proc/self/io", &proc_io_contents)) |
76 return false; | 293 return false; |
77 | 294 |
(...skipping 21 matching lines...) Expand all Loading... |
99 (*io_counters).WriteTransferCount = StringToInt64(tokenizer.token()); | 316 (*io_counters).WriteTransferCount = StringToInt64(tokenizer.token()); |
100 } | 317 } |
101 state = KEY_NAME; | 318 state = KEY_NAME; |
102 break; | 319 break; |
103 } | 320 } |
104 } | 321 } |
105 return true; | 322 return true; |
106 } | 323 } |
107 | 324 |
108 } // namespace process_util | 325 } // namespace process_util |
OLD | NEW |