1// Copyright 2017 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 "file_utils.h"
6
7#include <ctype.h>
8#include <errno.h>
9#include <fcntl.h>
10#include <stdio.h>
11#include <string.h>
12#include <sys/stat.h>
13#include <sys/types.h>
14
15namespace {
16
17bool IsNumeric(const char* str) {
18  if (!str[0])
19    return false;
20  for (const char* c = str; *c; c++) {
21    if (!isdigit(*c))
22      return false;
23  }
24  return true;
25}
26
27}  // namespace
28
29namespace file_utils {
30
31void ForEachPidInProcPath(const char* proc_path,
32                          std::function<void(int)> predicate) {
33  DIR* root_dir = opendir(proc_path);
34  ScopedDir autoclose(root_dir);
35  struct dirent* child_dir;
36  while ((child_dir = readdir(root_dir))) {
37    if (child_dir->d_type != DT_DIR || !IsNumeric(child_dir->d_name))
38      continue;
39    predicate(atoi(child_dir->d_name));
40  }
41}
42
43ssize_t ReadFile(const char* path, char* buf, size_t length) {
44  buf[0] = '\0';
45  int fd = open(path, O_RDONLY);
46  if (fd < 0 && errno == ENOENT)
47    return -1;
48  ScopedFD autoclose(fd);
49  size_t tot_read = 0;
50  do {
51    ssize_t rsize = read(fd, buf + tot_read, length - tot_read);
52    if (rsize == 0)
53      break;
54    if (rsize == -1 && errno == EINTR)
55      continue;
56    else if (rsize < 0)
57      return -1;
58    tot_read += static_cast<size_t>(rsize);
59  } while (tot_read < length);
60  buf[tot_read < length ? tot_read : length - 1] = '\0';
61  return tot_read;
62}
63
64bool ReadFileTrimmed(const char* path, char* buf, size_t length) {
65  ssize_t rsize = ReadFile(path, buf, length);
66  if (rsize < 0)
67    return false;
68  for (ssize_t i = 0; i < rsize; i++) {
69    const char c = buf[i];
70    if (c == '\0' || c == '\r' || c == '\n') {
71      buf[i] = '\0';
72      break;
73    }
74    buf[i] = isprint(c) ? c : '?';
75  }
76  return true;
77}
78
79ssize_t ReadProcFile(int pid, const char* proc_file, char* buf, size_t length) {
80  char proc_path[128];
81  snprintf(proc_path, sizeof(proc_path), "/proc/%d/%s", pid, proc_file);
82  return ReadFile(proc_path, buf, length);
83}
84
85// Reads a single-line proc file, stripping out any \0, \r, \n and replacing
86// non-printable charcters with '?'.
87bool ReadProcFileTrimmed(int pid,
88                         const char* proc_file,
89                         char* buf,
90                         size_t length) {
91  char proc_path[128];
92  snprintf(proc_path, sizeof(proc_path), "/proc/%d/%s", pid, proc_file);
93  return ReadFileTrimmed(proc_path, buf, length);
94}
95
96LineReader::LineReader(char* buf, size_t size)
97    : ptr_(buf), end_(buf + size) {
98}
99
100LineReader::~LineReader() {
101}
102
103const char* LineReader::NextLine() {
104  if (ptr_ >= end_)
105    return nullptr;
106  const char* cur = ptr_;
107  char* next = strchr(ptr_, '\n');
108  if (next) {
109    *next = '\0';
110    ptr_ = next + 1;
111  } else {
112    ptr_ = end_;
113  }
114  return cur;
115}
116
117}  // namespace file_utils
118