process_iterator_mac.cc 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // Copyright (c) 2013 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. #include "base/process/process_iterator.h"
  5. #include <errno.h>
  6. #include <stddef.h>
  7. #include <sys/sysctl.h>
  8. #include <sys/types.h>
  9. #include <unistd.h>
  10. #include "base/logging.h"
  11. #include "base/strings/string_split.h"
  12. #include "base/strings/string_util.h"
  13. namespace base {
  14. ProcessIterator::ProcessIterator(const ProcessFilter* filter)
  15. : index_of_kinfo_proc_(0),
  16. filter_(filter) {
  17. // Get a snapshot of all of my processes (yes, as we loop it can go stale, but
  18. // but trying to find where we were in a constantly changing list is basically
  19. // impossible.
  20. int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID,
  21. static_cast<int>(geteuid()) };
  22. // Since more processes could start between when we get the size and when
  23. // we get the list, we do a loop to keep trying until we get it.
  24. bool done = false;
  25. int try_num = 1;
  26. const int max_tries = 10;
  27. do {
  28. // Get the size of the buffer
  29. size_t len = 0;
  30. if (sysctl(mib, std::size(mib), NULL, &len, NULL, 0) < 0) {
  31. DLOG(ERROR) << "failed to get the size needed for the process list";
  32. kinfo_procs_.resize(0);
  33. done = true;
  34. } else {
  35. size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
  36. // Leave some spare room for process table growth (more could show up
  37. // between when we check and now)
  38. num_of_kinfo_proc += 16;
  39. kinfo_procs_.resize(num_of_kinfo_proc);
  40. len = num_of_kinfo_proc * sizeof(struct kinfo_proc);
  41. // Load the list of processes
  42. if (sysctl(mib, std::size(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) {
  43. // If we get a mem error, it just means we need a bigger buffer, so
  44. // loop around again. Anything else is a real error and give up.
  45. if (errno != ENOMEM) {
  46. DLOG(ERROR) << "failed to get the process list";
  47. kinfo_procs_.resize(0);
  48. done = true;
  49. }
  50. } else {
  51. // Got the list, just make sure we're sized exactly right
  52. kinfo_procs_.resize(len / sizeof(struct kinfo_proc));
  53. done = true;
  54. }
  55. }
  56. } while (!done && (try_num++ < max_tries));
  57. if (!done) {
  58. DLOG(ERROR) << "failed to collect the process list in a few tries";
  59. kinfo_procs_.resize(0);
  60. }
  61. }
  62. ProcessIterator::~ProcessIterator() {
  63. }
  64. bool ProcessIterator::CheckForNextProcess() {
  65. std::string data;
  66. for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) {
  67. kinfo_proc& kinfo = kinfo_procs_[index_of_kinfo_proc_];
  68. // Skip processes just awaiting collection
  69. if ((kinfo.kp_proc.p_pid > 0) && (kinfo.kp_proc.p_stat == SZOMB))
  70. continue;
  71. int mib[] = { CTL_KERN, KERN_PROCARGS, kinfo.kp_proc.p_pid };
  72. // Find out what size buffer we need.
  73. size_t data_len = 0;
  74. if (sysctl(mib, std::size(mib), NULL, &data_len, NULL, 0) < 0) {
  75. DVPLOG(1) << "failed to figure out the buffer size for a commandline";
  76. continue;
  77. }
  78. data.resize(data_len);
  79. if (sysctl(mib, std::size(mib), &data[0], &data_len, NULL, 0) < 0) {
  80. DVPLOG(1) << "failed to fetch a commandline";
  81. continue;
  82. }
  83. // |data| contains all the command line parameters of the process, separated
  84. // by blocks of one or more null characters. We tokenize |data| into a
  85. // vector of strings using '\0' as a delimiter and populate
  86. // |entry_.cmd_line_args_|.
  87. std::string delimiters;
  88. delimiters.push_back('\0');
  89. entry_.cmd_line_args_ = SplitString(data, delimiters,
  90. KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
  91. // |data| starts with the full executable path followed by a null character.
  92. // We search for the first instance of '\0' and extract everything before it
  93. // to populate |entry_.exe_file_|.
  94. size_t exec_name_end = data.find('\0');
  95. if (exec_name_end == std::string::npos) {
  96. DLOG(ERROR) << "command line data didn't match expected format";
  97. continue;
  98. }
  99. entry_.pid_ = kinfo.kp_proc.p_pid;
  100. entry_.ppid_ = kinfo.kp_eproc.e_ppid;
  101. entry_.gid_ = kinfo.kp_eproc.e_pgid;
  102. size_t last_slash = data.rfind('/', exec_name_end);
  103. if (last_slash == std::string::npos)
  104. entry_.exe_file_.assign(data, 0, exec_name_end);
  105. else
  106. entry_.exe_file_.assign(data, last_slash + 1,
  107. exec_name_end - last_slash - 1);
  108. // Start w/ the next entry next time through
  109. ++index_of_kinfo_proc_;
  110. // Done
  111. return true;
  112. }
  113. return false;
  114. }
  115. bool NamedProcessIterator::IncludeEntry() {
  116. return (executable_name_ == entry().exe_file() &&
  117. ProcessIterator::IncludeEntry());
  118. }
  119. } // namespace base