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 <stddef.h>
8
9#include "base/files/file_util.h"
10#include "base/logging.h"
11#include "base/process/internal_linux.h"
12#include "base/strings/string_split.h"
13#include "base/strings/string_util.h"
14#include "base/threading/thread_restrictions.h"
15
16namespace base {
17
18namespace {
19
20// Reads the |field_num|th field from |proc_stats|.
21// Returns an empty string on failure.
22// This version only handles VM_COMM and VM_STATE, which are the only fields
23// that are strings.
24std::string GetProcStatsFieldAsString(
25    const std::vector<std::string>& proc_stats,
26    internal::ProcStatsFields field_num) {
27  if (field_num < internal::VM_COMM || field_num > internal::VM_STATE) {
28    NOTREACHED();
29    return std::string();
30  }
31
32  if (proc_stats.size() > static_cast<size_t>(field_num))
33    return proc_stats[field_num];
34
35  NOTREACHED();
36  return 0;
37}
38
39// Reads /proc/<pid>/cmdline and populates |proc_cmd_line_args| with the command
40// line arguments. Returns true if successful.
41// Note: /proc/<pid>/cmdline contains command line arguments separated by single
42// null characters. We tokenize it into a vector of strings using '\0' as a
43// delimiter.
44bool GetProcCmdline(pid_t pid, std::vector<std::string>* proc_cmd_line_args) {
45  // Synchronously reading files in /proc is safe.
46  ThreadRestrictions::ScopedAllowIO allow_io;
47
48  FilePath cmd_line_file = internal::GetProcPidDir(pid).Append("cmdline");
49  std::string cmd_line;
50  if (!ReadFileToString(cmd_line_file, &cmd_line))
51    return false;
52  std::string delimiters;
53  delimiters.push_back('\0');
54  *proc_cmd_line_args = SplitString(cmd_line, delimiters, KEEP_WHITESPACE,
55                                    SPLIT_WANT_NONEMPTY);
56  return true;
57}
58
59}  // namespace
60
61ProcessIterator::ProcessIterator(const ProcessFilter* filter)
62    : filter_(filter) {
63  procfs_dir_ = opendir(internal::kProcDir);
64  if (!procfs_dir_) {
65    // On Android, SELinux may prevent reading /proc. See
66    // https://crbug.com/581517 for details.
67    PLOG(ERROR) << "opendir " << internal::kProcDir;
68  }
69}
70
71ProcessIterator::~ProcessIterator() {
72  if (procfs_dir_) {
73    closedir(procfs_dir_);
74    procfs_dir_ = nullptr;
75  }
76}
77
78bool ProcessIterator::CheckForNextProcess() {
79  // TODO(port): skip processes owned by different UID
80
81  if (!procfs_dir_) {
82    DLOG(ERROR) << "Skipping CheckForNextProcess(), no procfs_dir_";
83    return false;
84  }
85
86  pid_t pid = kNullProcessId;
87  std::vector<std::string> cmd_line_args;
88  std::string stats_data;
89  std::vector<std::string> proc_stats;
90
91  // Arbitrarily guess that there will never be more than 200 non-process
92  // files in /proc.  Hardy has 53 and Lucid has 61.
93  int skipped = 0;
94  const int kSkipLimit = 200;
95  while (skipped < kSkipLimit) {
96    dirent* slot = readdir(procfs_dir_);
97    // all done looking through /proc?
98    if (!slot)
99      return false;
100
101    // If not a process, keep looking for one.
102    pid = internal::ProcDirSlotToPid(slot->d_name);
103    if (!pid) {
104      skipped++;
105      continue;
106    }
107
108    if (!GetProcCmdline(pid, &cmd_line_args))
109      continue;
110
111    if (!internal::ReadProcStats(pid, &stats_data))
112      continue;
113    if (!internal::ParseProcStats(stats_data, &proc_stats))
114      continue;
115
116    std::string runstate =
117        GetProcStatsFieldAsString(proc_stats, internal::VM_STATE);
118    if (runstate.size() != 1) {
119      NOTREACHED();
120      continue;
121    }
122
123    // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped?
124    // Allowed values: D R S T Z
125    if (runstate[0] != 'Z')
126      break;
127
128    // Nope, it's a zombie; somebody isn't cleaning up after their children.
129    // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.)
130    // There could be a lot of zombies, can't really decrement i here.
131  }
132  if (skipped >= kSkipLimit) {
133    NOTREACHED();
134    return false;
135  }
136
137  entry_.pid_ = pid;
138  entry_.ppid_ = GetProcStatsFieldAsInt64(proc_stats, internal::VM_PPID);
139  entry_.gid_ = GetProcStatsFieldAsInt64(proc_stats, internal::VM_PGRP);
140  entry_.cmd_line_args_.assign(cmd_line_args.begin(), cmd_line_args.end());
141  entry_.exe_file_ = GetProcessExecutablePath(pid).BaseName().value();
142  return true;
143}
144
145bool NamedProcessIterator::IncludeEntry() {
146  if (executable_name_ != entry().exe_file())
147    return false;
148  return ProcessIterator::IncludeEntry();
149}
150
151}  // namespace base
152