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