| OLD | NEW |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 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 | 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 "chrome/browser/chromeos/system_logs/single_log_source.h" | 5 #include "chrome/browser/chromeos/system_logs/single_log_source.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/files/file_path.h" | 8 #include "base/files/file_path.h" |
| 9 #include "base/task_scheduler/post_task.h" | 9 #include "base/task_scheduler/post_task.h" |
| 10 #include "content/public/browser/browser_thread.h" | 10 #include "content/public/browser/browser_thread.h" |
| 11 | 11 |
| 12 namespace system_logs { | 12 namespace system_logs { |
| 13 | 13 |
| 14 namespace { | 14 namespace { |
| 15 | 15 |
| 16 const char kDefaultSystemLogDirPath[] = "/var/log"; | 16 constexpr char kDefaultSystemLogDirPath[] = "/var/log"; |
| 17 constexpr int kMaxNumAllowedLogRotationsDuringFileRead = 3; |
| 17 | 18 |
| 18 // Converts a logs source type to the corresponding file path, relative to the | 19 // Converts a logs source type to the corresponding file path, relative to the |
| 19 // base system log directory path. In the future, if non-file source types are | 20 // base system log directory path. In the future, if non-file source types are |
| 20 // added, this function should return an empty file path. | 21 // added, this function should return an empty file path. |
| 21 base::FilePath GetLogFileSourceRelativeFilePath( | 22 base::FilePath GetLogFileSourceRelativeFilePath( |
| 22 SingleLogSource::SupportedSource source) { | 23 SingleLogSource::SupportedSource source) { |
| 23 switch (source) { | 24 switch (source) { |
| 24 case SingleLogSource::SupportedSource::kMessages: | 25 case SingleLogSource::SupportedSource::kMessages: |
| 25 return base::FilePath("messages"); | 26 return base::FilePath("messages"); |
| 26 case SingleLogSource::SupportedSource::kUiLatest: | 27 case SingleLogSource::SupportedSource::kUiLatest: |
| 27 return base::FilePath("ui/ui.LATEST"); | 28 return base::FilePath("ui/ui.LATEST"); |
| 28 } | 29 } |
| 29 NOTREACHED(); | 30 NOTREACHED(); |
| 30 return base::FilePath(); | 31 return base::FilePath(); |
| 31 } | 32 } |
| 32 | 33 |
| 34 // Returns the inode value of file at |path|, or 0 if it doesn't exist or is |
| 35 // otherwise unable to be accessed for file system info. |
| 36 ino_t GetInodeValue(const base::FilePath& path) { |
| 37 struct stat file_stats; |
| 38 if (stat(path.value().c_str(), &file_stats) != 0) |
| 39 return 0; |
| 40 return file_stats.st_ino; |
| 41 } |
| 42 |
| 33 // Attempts to store a string |value| in |*response| under |key|. If there is | 43 // Attempts to store a string |value| in |*response| under |key|. If there is |
| 34 // already a string in |*response| under |key|, appends |value| to the existing | 44 // already a string in |*response| under |key|, appends |value| to the existing |
| 35 // string value. | 45 // string value. |
| 36 void AppendToSystemLogsResponse(SystemLogsResponse* response, | 46 void AppendToSystemLogsResponse(SystemLogsResponse* response, |
| 37 const std::string& key, | 47 const std::string& key, |
| 38 const std::string& value) { | 48 const std::string& value) { |
| 39 auto iter = response->find(key); | 49 auto iter = response->find(key); |
| 40 if (iter == response->end()) | 50 if (iter == response->end()) |
| 41 response->emplace(key, value); | 51 response->emplace(key, value); |
| 42 else | 52 else |
| 43 iter->second += value; | 53 iter->second += value; |
| 44 } | 54 } |
| 45 | 55 |
| 46 } // namespace | 56 } // namespace |
| 47 | 57 |
| 48 SingleLogSource::SingleLogSource(SupportedSource source) | 58 SingleLogSource::SingleLogSource(SupportedSource source) |
| 49 : SystemLogsSource(GetLogFileSourceRelativeFilePath(source).value()), | 59 : SystemLogsSource(GetLogFileSourceRelativeFilePath(source).value()), |
| 50 log_file_dir_path_(kDefaultSystemLogDirPath), | 60 log_file_dir_path_(kDefaultSystemLogDirPath), |
| 51 num_bytes_read_(0), | 61 num_bytes_read_(0), |
| 62 file_inode_(0), |
| 52 weak_ptr_factory_(this) {} | 63 weak_ptr_factory_(this) {} |
| 53 | 64 |
| 54 SingleLogSource::~SingleLogSource() {} | 65 SingleLogSource::~SingleLogSource() {} |
| 55 | 66 |
| 56 void SingleLogSource::Fetch(const SysLogsSourceCallback& callback) { | 67 void SingleLogSource::Fetch(const SysLogsSourceCallback& callback) { |
| 57 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 68 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 58 DCHECK(!callback.is_null()); | 69 DCHECK(!callback.is_null()); |
| 59 | 70 |
| 60 SystemLogsResponse* response = new SystemLogsResponse; | 71 SystemLogsResponse* response = new SystemLogsResponse; |
| 61 base::PostTaskWithTraitsAndReply( | 72 base::PostTaskWithTraitsAndReply( |
| 62 FROM_HERE, | 73 FROM_HERE, |
| 63 base::TaskTraits(base::MayBlock(), base::TaskPriority::BACKGROUND), | 74 base::TaskTraits(base::MayBlock(), base::TaskPriority::BACKGROUND), |
| 64 base::Bind(&SingleLogSource::ReadFile, weak_ptr_factory_.GetWeakPtr(), | 75 base::Bind(&SingleLogSource::ReadFile, weak_ptr_factory_.GetWeakPtr(), |
| 65 response), | 76 kMaxNumAllowedLogRotationsDuringFileRead, response), |
| 66 base::Bind(callback, base::Owned(response))); | 77 base::Bind(callback, base::Owned(response))); |
| 67 } | 78 } |
| 68 | 79 |
| 69 void SingleLogSource::ReadFile(SystemLogsResponse* result) { | 80 base::FilePath SingleLogSource::GetLogFilePath() const { |
| 81 return base::FilePath(log_file_dir_path_).Append(source_name()); |
| 82 } |
| 83 |
| 84 void SingleLogSource::ReadFile(size_t num_rotations_allowed, |
| 85 SystemLogsResponse* result) { |
| 70 // Attempt to open the file if it was not previously opened. | 86 // Attempt to open the file if it was not previously opened. |
| 71 if (!file_.IsValid()) { | 87 if (!file_.IsValid()) { |
| 72 file_.Initialize(base::FilePath(log_file_dir_path_).Append(source_name()), | 88 file_.Initialize(GetLogFilePath(), |
| 73 base::File::FLAG_OPEN | base::File::FLAG_READ); | 89 base::File::FLAG_OPEN | base::File::FLAG_READ); |
| 74 if (!file_.IsValid()) | 90 if (!file_.IsValid()) |
| 75 return; | 91 return; |
| 92 |
| 93 file_inode_ = GetInodeValue(GetLogFilePath()); |
| 76 } | 94 } |
| 77 | 95 |
| 78 // Check for file size reset. | 96 // Check for file size reset. |
| 79 const size_t length = file_.GetLength(); | 97 const size_t length = file_.GetLength(); |
| 80 if (length < num_bytes_read_) { | 98 if (length < num_bytes_read_) { |
| 81 num_bytes_read_ = 0; | 99 num_bytes_read_ = 0; |
| 82 file_.Seek(base::File::FROM_BEGIN, 0); | 100 file_.Seek(base::File::FROM_BEGIN, 0); |
| 83 } | 101 } |
| 84 | 102 |
| 85 // Read from file until end. | 103 // Read from file until end. |
| 86 const size_t size_to_read = length - num_bytes_read_; | 104 const size_t size_to_read = length - num_bytes_read_; |
| 87 std::string result_string; | 105 std::string result_string; |
| 88 result_string.resize(size_to_read); | 106 result_string.resize(size_to_read); |
| 89 size_t size_read = file_.ReadAtCurrentPos(&result_string[0], size_to_read); | 107 size_t size_read = file_.ReadAtCurrentPos(&result_string[0], size_to_read); |
| 90 result_string.resize(size_read); | 108 result_string.resize(size_read); |
| 91 | 109 |
| 92 // The reader may only read complete lines. | 110 const bool file_was_rotated = file_inode_ != GetInodeValue(GetLogFilePath()); |
| 93 if (result_string.empty() || result_string.back() != '\n') { | 111 const bool should_handle_file_rotation = |
| 112 file_was_rotated && num_rotations_allowed > 0; |
| 113 |
| 114 // The reader may only read complete lines. The exception is when there is a |
| 115 // rotation, in which case all the remaining contents of the old log file |
| 116 // should be read before moving on to read the new log file. |
| 117 if ((result_string.empty() || result_string.back() != '\n') && |
| 118 !should_handle_file_rotation) { |
| 94 // If an incomplete line was read, return only the part that includes whole | 119 // If an incomplete line was read, return only the part that includes whole |
| 95 // lines. | 120 // lines. |
| 96 size_t last_newline_pos = result_string.find_last_of('\n'); | 121 size_t last_newline_pos = result_string.find_last_of('\n'); |
| 97 if (last_newline_pos == std::string::npos) { | 122 if (last_newline_pos == std::string::npos) { |
| 98 file_.Seek(base::File::FROM_CURRENT, -size_read); | 123 file_.Seek(base::File::FROM_CURRENT, -size_read); |
| 99 AppendToSystemLogsResponse(result, source_name(), ""); | 124 AppendToSystemLogsResponse(result, source_name(), ""); |
| 100 return; | 125 return; |
| 101 } | 126 } |
| 102 // The part of the string that will be returned includes the newline itself. | 127 // The part of the string that will be returned includes the newline itself. |
| 103 size_t adjusted_size_read = last_newline_pos + 1; | 128 size_t adjusted_size_read = last_newline_pos + 1; |
| 104 file_.Seek(base::File::FROM_CURRENT, -size_read + adjusted_size_read); | 129 file_.Seek(base::File::FROM_CURRENT, -size_read + adjusted_size_read); |
| 105 result_string.resize(adjusted_size_read); | 130 result_string.resize(adjusted_size_read); |
| 106 | 131 |
| 107 // Update |size_read| to reflect that the read was only up to the last | 132 // Update |size_read| to reflect that the read was only up to the last |
| 108 // newline. | 133 // newline. |
| 109 size_read = adjusted_size_read; | 134 size_read = adjusted_size_read; |
| 110 } | 135 } |
| 111 | 136 |
| 112 num_bytes_read_ += size_read; | 137 num_bytes_read_ += size_read; |
| 113 | 138 |
| 114 // Pass it back to the callback. | 139 // Pass it back to the callback. |
| 115 AppendToSystemLogsResponse(result, source_name(), | 140 AppendToSystemLogsResponse(result, source_name(), |
| 116 anonymizer_.Anonymize(result_string)); | 141 anonymizer_.Anonymize(result_string)); |
| 142 |
| 143 // If the file was rotated, close the file handle and call this function |
| 144 // again, to read from the new file. |
| 145 if (should_handle_file_rotation) { |
| 146 file_.Close(); |
| 147 num_bytes_read_ = 0; |
| 148 file_inode_ = 0; |
| 149 ReadFile(num_rotations_allowed - 1, result); |
| 150 } |
| 117 } | 151 } |
| 118 | 152 |
| 119 } // namespace system_logs | 153 } // namespace system_logs |
| OLD | NEW |