1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker * Copyright 2015 The WebRTC Project Authors. All rights reserved.
3*d9f75844SAndroid Build Coastguard Worker *
4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker */
10*d9f75844SAndroid Build Coastguard Worker
11*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/file_rotating_stream.h"
12*d9f75844SAndroid Build Coastguard Worker
13*d9f75844SAndroid Build Coastguard Worker #include <cstdio>
14*d9f75844SAndroid Build Coastguard Worker #include <string>
15*d9f75844SAndroid Build Coastguard Worker #include <utility>
16*d9f75844SAndroid Build Coastguard Worker
17*d9f75844SAndroid Build Coastguard Worker #include "absl/strings/string_view.h"
18*d9f75844SAndroid Build Coastguard Worker
19*d9f75844SAndroid Build Coastguard Worker #if defined(WEBRTC_WIN)
20*d9f75844SAndroid Build Coastguard Worker #include <windows.h>
21*d9f75844SAndroid Build Coastguard Worker
22*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/string_utils.h"
23*d9f75844SAndroid Build Coastguard Worker #else
24*d9f75844SAndroid Build Coastguard Worker #include <dirent.h>
25*d9f75844SAndroid Build Coastguard Worker #include <sys/stat.h>
26*d9f75844SAndroid Build Coastguard Worker #include <unistd.h>
27*d9f75844SAndroid Build Coastguard Worker #endif // WEBRTC_WIN
28*d9f75844SAndroid Build Coastguard Worker
29*d9f75844SAndroid Build Coastguard Worker #include "absl/algorithm/container.h"
30*d9f75844SAndroid Build Coastguard Worker #include "absl/strings/match.h"
31*d9f75844SAndroid Build Coastguard Worker #include "absl/types/optional.h"
32*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
33*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/logging.h"
34*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/strings/string_builder.h"
35*d9f75844SAndroid Build Coastguard Worker
36*d9f75844SAndroid Build Coastguard Worker // Note: We use fprintf for logging in the write paths of this stream to avoid
37*d9f75844SAndroid Build Coastguard Worker // infinite loops when logging.
38*d9f75844SAndroid Build Coastguard Worker
39*d9f75844SAndroid Build Coastguard Worker namespace rtc {
40*d9f75844SAndroid Build Coastguard Worker
41*d9f75844SAndroid Build Coastguard Worker namespace {
42*d9f75844SAndroid Build Coastguard Worker
43*d9f75844SAndroid Build Coastguard Worker const char kCallSessionLogPrefix[] = "webrtc_log";
44*d9f75844SAndroid Build Coastguard Worker
45*d9f75844SAndroid Build Coastguard Worker std::string AddTrailingPathDelimiterIfNeeded(absl::string_view directory);
46*d9f75844SAndroid Build Coastguard Worker
47*d9f75844SAndroid Build Coastguard Worker // `dir` must have a trailing delimiter. `prefix` must not include wild card
48*d9f75844SAndroid Build Coastguard Worker // characters.
49*d9f75844SAndroid Build Coastguard Worker std::vector<std::string> GetFilesWithPrefix(absl::string_view directory,
50*d9f75844SAndroid Build Coastguard Worker absl::string_view prefix);
51*d9f75844SAndroid Build Coastguard Worker bool DeleteFile(absl::string_view file);
52*d9f75844SAndroid Build Coastguard Worker bool MoveFile(absl::string_view old_file, absl::string_view new_file);
53*d9f75844SAndroid Build Coastguard Worker bool IsFile(absl::string_view file);
54*d9f75844SAndroid Build Coastguard Worker bool IsFolder(absl::string_view file);
55*d9f75844SAndroid Build Coastguard Worker absl::optional<size_t> GetFileSize(absl::string_view file);
56*d9f75844SAndroid Build Coastguard Worker
57*d9f75844SAndroid Build Coastguard Worker #if defined(WEBRTC_WIN)
58*d9f75844SAndroid Build Coastguard Worker
AddTrailingPathDelimiterIfNeeded(absl::string_view directory)59*d9f75844SAndroid Build Coastguard Worker std::string AddTrailingPathDelimiterIfNeeded(absl::string_view directory) {
60*d9f75844SAndroid Build Coastguard Worker if (absl::EndsWith(directory, "\\")) {
61*d9f75844SAndroid Build Coastguard Worker return std::string(directory);
62*d9f75844SAndroid Build Coastguard Worker }
63*d9f75844SAndroid Build Coastguard Worker return std::string(directory) + "\\";
64*d9f75844SAndroid Build Coastguard Worker }
65*d9f75844SAndroid Build Coastguard Worker
GetFilesWithPrefix(absl::string_view directory,absl::string_view prefix)66*d9f75844SAndroid Build Coastguard Worker std::vector<std::string> GetFilesWithPrefix(absl::string_view directory,
67*d9f75844SAndroid Build Coastguard Worker absl::string_view prefix) {
68*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(absl::EndsWith(directory, "\\"));
69*d9f75844SAndroid Build Coastguard Worker WIN32_FIND_DATAW data;
70*d9f75844SAndroid Build Coastguard Worker HANDLE handle;
71*d9f75844SAndroid Build Coastguard Worker StringBuilder pattern_builder{directory};
72*d9f75844SAndroid Build Coastguard Worker pattern_builder << prefix << "*";
73*d9f75844SAndroid Build Coastguard Worker handle = ::FindFirstFileW(ToUtf16(pattern_builder.str()).c_str(), &data);
74*d9f75844SAndroid Build Coastguard Worker if (handle == INVALID_HANDLE_VALUE)
75*d9f75844SAndroid Build Coastguard Worker return {};
76*d9f75844SAndroid Build Coastguard Worker
77*d9f75844SAndroid Build Coastguard Worker std::vector<std::string> file_list;
78*d9f75844SAndroid Build Coastguard Worker do {
79*d9f75844SAndroid Build Coastguard Worker StringBuilder file_builder{directory};
80*d9f75844SAndroid Build Coastguard Worker file_builder << ToUtf8(data.cFileName);
81*d9f75844SAndroid Build Coastguard Worker file_list.emplace_back(file_builder.Release());
82*d9f75844SAndroid Build Coastguard Worker } while (::FindNextFileW(handle, &data) == TRUE);
83*d9f75844SAndroid Build Coastguard Worker
84*d9f75844SAndroid Build Coastguard Worker ::FindClose(handle);
85*d9f75844SAndroid Build Coastguard Worker return file_list;
86*d9f75844SAndroid Build Coastguard Worker }
87*d9f75844SAndroid Build Coastguard Worker
DeleteFile(absl::string_view file)88*d9f75844SAndroid Build Coastguard Worker bool DeleteFile(absl::string_view file) {
89*d9f75844SAndroid Build Coastguard Worker return ::DeleteFileW(ToUtf16(file).c_str()) != 0;
90*d9f75844SAndroid Build Coastguard Worker }
91*d9f75844SAndroid Build Coastguard Worker
MoveFile(absl::string_view old_file,absl::string_view new_file)92*d9f75844SAndroid Build Coastguard Worker bool MoveFile(absl::string_view old_file, absl::string_view new_file) {
93*d9f75844SAndroid Build Coastguard Worker return ::MoveFileW(ToUtf16(old_file).c_str(), ToUtf16(new_file).c_str()) != 0;
94*d9f75844SAndroid Build Coastguard Worker }
95*d9f75844SAndroid Build Coastguard Worker
IsFile(absl::string_view file)96*d9f75844SAndroid Build Coastguard Worker bool IsFile(absl::string_view file) {
97*d9f75844SAndroid Build Coastguard Worker WIN32_FILE_ATTRIBUTE_DATA data = {0};
98*d9f75844SAndroid Build Coastguard Worker if (0 == ::GetFileAttributesExW(ToUtf16(file).c_str(), GetFileExInfoStandard,
99*d9f75844SAndroid Build Coastguard Worker &data))
100*d9f75844SAndroid Build Coastguard Worker return false;
101*d9f75844SAndroid Build Coastguard Worker return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
102*d9f75844SAndroid Build Coastguard Worker }
103*d9f75844SAndroid Build Coastguard Worker
IsFolder(absl::string_view file)104*d9f75844SAndroid Build Coastguard Worker bool IsFolder(absl::string_view file) {
105*d9f75844SAndroid Build Coastguard Worker WIN32_FILE_ATTRIBUTE_DATA data = {0};
106*d9f75844SAndroid Build Coastguard Worker if (0 == ::GetFileAttributesExW(ToUtf16(file).c_str(), GetFileExInfoStandard,
107*d9f75844SAndroid Build Coastguard Worker &data))
108*d9f75844SAndroid Build Coastguard Worker return false;
109*d9f75844SAndroid Build Coastguard Worker return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ==
110*d9f75844SAndroid Build Coastguard Worker FILE_ATTRIBUTE_DIRECTORY;
111*d9f75844SAndroid Build Coastguard Worker }
112*d9f75844SAndroid Build Coastguard Worker
GetFileSize(absl::string_view file)113*d9f75844SAndroid Build Coastguard Worker absl::optional<size_t> GetFileSize(absl::string_view file) {
114*d9f75844SAndroid Build Coastguard Worker WIN32_FILE_ATTRIBUTE_DATA data = {0};
115*d9f75844SAndroid Build Coastguard Worker if (::GetFileAttributesExW(ToUtf16(file).c_str(), GetFileExInfoStandard,
116*d9f75844SAndroid Build Coastguard Worker &data) == 0)
117*d9f75844SAndroid Build Coastguard Worker return absl::nullopt;
118*d9f75844SAndroid Build Coastguard Worker return data.nFileSizeLow;
119*d9f75844SAndroid Build Coastguard Worker }
120*d9f75844SAndroid Build Coastguard Worker
121*d9f75844SAndroid Build Coastguard Worker #else // defined(WEBRTC_WIN)
122*d9f75844SAndroid Build Coastguard Worker
AddTrailingPathDelimiterIfNeeded(absl::string_view directory)123*d9f75844SAndroid Build Coastguard Worker std::string AddTrailingPathDelimiterIfNeeded(absl::string_view directory) {
124*d9f75844SAndroid Build Coastguard Worker if (absl::EndsWith(directory, "/")) {
125*d9f75844SAndroid Build Coastguard Worker return std::string(directory);
126*d9f75844SAndroid Build Coastguard Worker }
127*d9f75844SAndroid Build Coastguard Worker return std::string(directory) + "/";
128*d9f75844SAndroid Build Coastguard Worker }
129*d9f75844SAndroid Build Coastguard Worker
GetFilesWithPrefix(absl::string_view directory,absl::string_view prefix)130*d9f75844SAndroid Build Coastguard Worker std::vector<std::string> GetFilesWithPrefix(absl::string_view directory,
131*d9f75844SAndroid Build Coastguard Worker absl::string_view prefix) {
132*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(absl::EndsWith(directory, "/"));
133*d9f75844SAndroid Build Coastguard Worker std::string directory_str(directory);
134*d9f75844SAndroid Build Coastguard Worker DIR* dir = ::opendir(directory_str.c_str());
135*d9f75844SAndroid Build Coastguard Worker if (dir == nullptr)
136*d9f75844SAndroid Build Coastguard Worker return {};
137*d9f75844SAndroid Build Coastguard Worker std::vector<std::string> file_list;
138*d9f75844SAndroid Build Coastguard Worker for (struct dirent* dirent = ::readdir(dir); dirent;
139*d9f75844SAndroid Build Coastguard Worker dirent = ::readdir(dir)) {
140*d9f75844SAndroid Build Coastguard Worker std::string name = dirent->d_name;
141*d9f75844SAndroid Build Coastguard Worker if (name.compare(0, prefix.size(), prefix.data(), prefix.size()) == 0) {
142*d9f75844SAndroid Build Coastguard Worker file_list.emplace_back(directory_str + name);
143*d9f75844SAndroid Build Coastguard Worker }
144*d9f75844SAndroid Build Coastguard Worker }
145*d9f75844SAndroid Build Coastguard Worker ::closedir(dir);
146*d9f75844SAndroid Build Coastguard Worker return file_list;
147*d9f75844SAndroid Build Coastguard Worker }
148*d9f75844SAndroid Build Coastguard Worker
DeleteFile(absl::string_view file)149*d9f75844SAndroid Build Coastguard Worker bool DeleteFile(absl::string_view file) {
150*d9f75844SAndroid Build Coastguard Worker return ::unlink(std::string(file).c_str()) == 0;
151*d9f75844SAndroid Build Coastguard Worker }
152*d9f75844SAndroid Build Coastguard Worker
MoveFile(absl::string_view old_file,absl::string_view new_file)153*d9f75844SAndroid Build Coastguard Worker bool MoveFile(absl::string_view old_file, absl::string_view new_file) {
154*d9f75844SAndroid Build Coastguard Worker return ::rename(std::string(old_file).c_str(),
155*d9f75844SAndroid Build Coastguard Worker std::string(new_file).c_str()) == 0;
156*d9f75844SAndroid Build Coastguard Worker }
157*d9f75844SAndroid Build Coastguard Worker
IsFile(absl::string_view file)158*d9f75844SAndroid Build Coastguard Worker bool IsFile(absl::string_view file) {
159*d9f75844SAndroid Build Coastguard Worker struct stat st;
160*d9f75844SAndroid Build Coastguard Worker int res = ::stat(std::string(file).c_str(), &st);
161*d9f75844SAndroid Build Coastguard Worker // Treat symlinks, named pipes, etc. all as files.
162*d9f75844SAndroid Build Coastguard Worker return res == 0 && !S_ISDIR(st.st_mode);
163*d9f75844SAndroid Build Coastguard Worker }
164*d9f75844SAndroid Build Coastguard Worker
IsFolder(absl::string_view file)165*d9f75844SAndroid Build Coastguard Worker bool IsFolder(absl::string_view file) {
166*d9f75844SAndroid Build Coastguard Worker struct stat st;
167*d9f75844SAndroid Build Coastguard Worker int res = ::stat(std::string(file).c_str(), &st);
168*d9f75844SAndroid Build Coastguard Worker return res == 0 && S_ISDIR(st.st_mode);
169*d9f75844SAndroid Build Coastguard Worker }
170*d9f75844SAndroid Build Coastguard Worker
GetFileSize(absl::string_view file)171*d9f75844SAndroid Build Coastguard Worker absl::optional<size_t> GetFileSize(absl::string_view file) {
172*d9f75844SAndroid Build Coastguard Worker struct stat st;
173*d9f75844SAndroid Build Coastguard Worker if (::stat(std::string(file).c_str(), &st) != 0)
174*d9f75844SAndroid Build Coastguard Worker return absl::nullopt;
175*d9f75844SAndroid Build Coastguard Worker return st.st_size;
176*d9f75844SAndroid Build Coastguard Worker }
177*d9f75844SAndroid Build Coastguard Worker
178*d9f75844SAndroid Build Coastguard Worker #endif
179*d9f75844SAndroid Build Coastguard Worker
180*d9f75844SAndroid Build Coastguard Worker } // namespace
181*d9f75844SAndroid Build Coastguard Worker
FileRotatingStream(absl::string_view dir_path,absl::string_view file_prefix,size_t max_file_size,size_t num_files)182*d9f75844SAndroid Build Coastguard Worker FileRotatingStream::FileRotatingStream(absl::string_view dir_path,
183*d9f75844SAndroid Build Coastguard Worker absl::string_view file_prefix,
184*d9f75844SAndroid Build Coastguard Worker size_t max_file_size,
185*d9f75844SAndroid Build Coastguard Worker size_t num_files)
186*d9f75844SAndroid Build Coastguard Worker : dir_path_(AddTrailingPathDelimiterIfNeeded(dir_path)),
187*d9f75844SAndroid Build Coastguard Worker file_prefix_(file_prefix),
188*d9f75844SAndroid Build Coastguard Worker max_file_size_(max_file_size),
189*d9f75844SAndroid Build Coastguard Worker current_file_index_(0),
190*d9f75844SAndroid Build Coastguard Worker rotation_index_(0),
191*d9f75844SAndroid Build Coastguard Worker current_bytes_written_(0),
192*d9f75844SAndroid Build Coastguard Worker disable_buffering_(false) {
193*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(max_file_size, 0);
194*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(num_files, 1);
195*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(IsFolder(dir_path));
196*d9f75844SAndroid Build Coastguard Worker file_names_.clear();
197*d9f75844SAndroid Build Coastguard Worker for (size_t i = 0; i < num_files; ++i) {
198*d9f75844SAndroid Build Coastguard Worker file_names_.push_back(GetFilePath(i, num_files));
199*d9f75844SAndroid Build Coastguard Worker }
200*d9f75844SAndroid Build Coastguard Worker rotation_index_ = num_files - 1;
201*d9f75844SAndroid Build Coastguard Worker }
202*d9f75844SAndroid Build Coastguard Worker
~FileRotatingStream()203*d9f75844SAndroid Build Coastguard Worker FileRotatingStream::~FileRotatingStream() {}
204*d9f75844SAndroid Build Coastguard Worker
IsOpen() const205*d9f75844SAndroid Build Coastguard Worker bool FileRotatingStream::IsOpen() const {
206*d9f75844SAndroid Build Coastguard Worker return file_.is_open();
207*d9f75844SAndroid Build Coastguard Worker }
208*d9f75844SAndroid Build Coastguard Worker
Write(const void * data,size_t data_len)209*d9f75844SAndroid Build Coastguard Worker bool FileRotatingStream::Write(const void* data, size_t data_len) {
210*d9f75844SAndroid Build Coastguard Worker if (!file_.is_open()) {
211*d9f75844SAndroid Build Coastguard Worker std::fprintf(stderr, "Open() must be called before Write.\n");
212*d9f75844SAndroid Build Coastguard Worker return false;
213*d9f75844SAndroid Build Coastguard Worker }
214*d9f75844SAndroid Build Coastguard Worker while (data_len > 0) {
215*d9f75844SAndroid Build Coastguard Worker // Write as much as will fit in to the current file.
216*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_LT(current_bytes_written_, max_file_size_);
217*d9f75844SAndroid Build Coastguard Worker size_t remaining_bytes = max_file_size_ - current_bytes_written_;
218*d9f75844SAndroid Build Coastguard Worker size_t write_length = std::min(data_len, remaining_bytes);
219*d9f75844SAndroid Build Coastguard Worker
220*d9f75844SAndroid Build Coastguard Worker if (!file_.Write(data, write_length)) {
221*d9f75844SAndroid Build Coastguard Worker return false;
222*d9f75844SAndroid Build Coastguard Worker }
223*d9f75844SAndroid Build Coastguard Worker if (disable_buffering_ && !file_.Flush()) {
224*d9f75844SAndroid Build Coastguard Worker return false;
225*d9f75844SAndroid Build Coastguard Worker }
226*d9f75844SAndroid Build Coastguard Worker
227*d9f75844SAndroid Build Coastguard Worker current_bytes_written_ += write_length;
228*d9f75844SAndroid Build Coastguard Worker
229*d9f75844SAndroid Build Coastguard Worker // If we're done with this file, rotate it out.
230*d9f75844SAndroid Build Coastguard Worker if (current_bytes_written_ >= max_file_size_) {
231*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_EQ(current_bytes_written_, max_file_size_);
232*d9f75844SAndroid Build Coastguard Worker RotateFiles();
233*d9f75844SAndroid Build Coastguard Worker }
234*d9f75844SAndroid Build Coastguard Worker data_len -= write_length;
235*d9f75844SAndroid Build Coastguard Worker data =
236*d9f75844SAndroid Build Coastguard Worker static_cast<const void*>(static_cast<const char*>(data) + write_length);
237*d9f75844SAndroid Build Coastguard Worker }
238*d9f75844SAndroid Build Coastguard Worker return true;
239*d9f75844SAndroid Build Coastguard Worker }
240*d9f75844SAndroid Build Coastguard Worker
Flush()241*d9f75844SAndroid Build Coastguard Worker bool FileRotatingStream::Flush() {
242*d9f75844SAndroid Build Coastguard Worker if (!file_.is_open()) {
243*d9f75844SAndroid Build Coastguard Worker return false;
244*d9f75844SAndroid Build Coastguard Worker }
245*d9f75844SAndroid Build Coastguard Worker return file_.Flush();
246*d9f75844SAndroid Build Coastguard Worker }
247*d9f75844SAndroid Build Coastguard Worker
Close()248*d9f75844SAndroid Build Coastguard Worker void FileRotatingStream::Close() {
249*d9f75844SAndroid Build Coastguard Worker CloseCurrentFile();
250*d9f75844SAndroid Build Coastguard Worker }
251*d9f75844SAndroid Build Coastguard Worker
Open()252*d9f75844SAndroid Build Coastguard Worker bool FileRotatingStream::Open() {
253*d9f75844SAndroid Build Coastguard Worker // Delete existing files when opening for write.
254*d9f75844SAndroid Build Coastguard Worker std::vector<std::string> matching_files =
255*d9f75844SAndroid Build Coastguard Worker GetFilesWithPrefix(dir_path_, file_prefix_);
256*d9f75844SAndroid Build Coastguard Worker for (const auto& matching_file : matching_files) {
257*d9f75844SAndroid Build Coastguard Worker if (!DeleteFile(matching_file)) {
258*d9f75844SAndroid Build Coastguard Worker std::fprintf(stderr, "Failed to delete: %s\n", matching_file.c_str());
259*d9f75844SAndroid Build Coastguard Worker }
260*d9f75844SAndroid Build Coastguard Worker }
261*d9f75844SAndroid Build Coastguard Worker return OpenCurrentFile();
262*d9f75844SAndroid Build Coastguard Worker }
263*d9f75844SAndroid Build Coastguard Worker
DisableBuffering()264*d9f75844SAndroid Build Coastguard Worker bool FileRotatingStream::DisableBuffering() {
265*d9f75844SAndroid Build Coastguard Worker disable_buffering_ = true;
266*d9f75844SAndroid Build Coastguard Worker return true;
267*d9f75844SAndroid Build Coastguard Worker }
268*d9f75844SAndroid Build Coastguard Worker
GetFilePath(size_t index) const269*d9f75844SAndroid Build Coastguard Worker std::string FileRotatingStream::GetFilePath(size_t index) const {
270*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_LT(index, file_names_.size());
271*d9f75844SAndroid Build Coastguard Worker return file_names_[index];
272*d9f75844SAndroid Build Coastguard Worker }
273*d9f75844SAndroid Build Coastguard Worker
OpenCurrentFile()274*d9f75844SAndroid Build Coastguard Worker bool FileRotatingStream::OpenCurrentFile() {
275*d9f75844SAndroid Build Coastguard Worker CloseCurrentFile();
276*d9f75844SAndroid Build Coastguard Worker
277*d9f75844SAndroid Build Coastguard Worker // Opens the appropriate file in the appropriate mode.
278*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_LT(current_file_index_, file_names_.size());
279*d9f75844SAndroid Build Coastguard Worker std::string file_path = file_names_[current_file_index_];
280*d9f75844SAndroid Build Coastguard Worker
281*d9f75844SAndroid Build Coastguard Worker // We should always be writing to the zero-th file.
282*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_EQ(current_file_index_, 0);
283*d9f75844SAndroid Build Coastguard Worker int error;
284*d9f75844SAndroid Build Coastguard Worker file_ = webrtc::FileWrapper::OpenWriteOnly(file_path, &error);
285*d9f75844SAndroid Build Coastguard Worker if (!file_.is_open()) {
286*d9f75844SAndroid Build Coastguard Worker std::fprintf(stderr, "Failed to open: %s Error: %d\n", file_path.c_str(),
287*d9f75844SAndroid Build Coastguard Worker error);
288*d9f75844SAndroid Build Coastguard Worker return false;
289*d9f75844SAndroid Build Coastguard Worker }
290*d9f75844SAndroid Build Coastguard Worker return true;
291*d9f75844SAndroid Build Coastguard Worker }
292*d9f75844SAndroid Build Coastguard Worker
CloseCurrentFile()293*d9f75844SAndroid Build Coastguard Worker void FileRotatingStream::CloseCurrentFile() {
294*d9f75844SAndroid Build Coastguard Worker if (!file_.is_open()) {
295*d9f75844SAndroid Build Coastguard Worker return;
296*d9f75844SAndroid Build Coastguard Worker }
297*d9f75844SAndroid Build Coastguard Worker current_bytes_written_ = 0;
298*d9f75844SAndroid Build Coastguard Worker file_.Close();
299*d9f75844SAndroid Build Coastguard Worker }
300*d9f75844SAndroid Build Coastguard Worker
RotateFiles()301*d9f75844SAndroid Build Coastguard Worker void FileRotatingStream::RotateFiles() {
302*d9f75844SAndroid Build Coastguard Worker CloseCurrentFile();
303*d9f75844SAndroid Build Coastguard Worker // Rotates the files by deleting the file at `rotation_index_`, which is the
304*d9f75844SAndroid Build Coastguard Worker // oldest file and then renaming the newer files to have an incremented index.
305*d9f75844SAndroid Build Coastguard Worker // See header file comments for example.
306*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_LT(rotation_index_, file_names_.size());
307*d9f75844SAndroid Build Coastguard Worker std::string file_to_delete = file_names_[rotation_index_];
308*d9f75844SAndroid Build Coastguard Worker if (IsFile(file_to_delete)) {
309*d9f75844SAndroid Build Coastguard Worker if (!DeleteFile(file_to_delete)) {
310*d9f75844SAndroid Build Coastguard Worker std::fprintf(stderr, "Failed to delete: %s\n", file_to_delete.c_str());
311*d9f75844SAndroid Build Coastguard Worker }
312*d9f75844SAndroid Build Coastguard Worker }
313*d9f75844SAndroid Build Coastguard Worker for (auto i = rotation_index_; i > 0; --i) {
314*d9f75844SAndroid Build Coastguard Worker std::string rotated_name = file_names_[i];
315*d9f75844SAndroid Build Coastguard Worker std::string unrotated_name = file_names_[i - 1];
316*d9f75844SAndroid Build Coastguard Worker if (IsFile(unrotated_name)) {
317*d9f75844SAndroid Build Coastguard Worker if (!MoveFile(unrotated_name, rotated_name)) {
318*d9f75844SAndroid Build Coastguard Worker std::fprintf(stderr, "Failed to move: %s to %s\n",
319*d9f75844SAndroid Build Coastguard Worker unrotated_name.c_str(), rotated_name.c_str());
320*d9f75844SAndroid Build Coastguard Worker }
321*d9f75844SAndroid Build Coastguard Worker }
322*d9f75844SAndroid Build Coastguard Worker }
323*d9f75844SAndroid Build Coastguard Worker // Create a new file for 0th index.
324*d9f75844SAndroid Build Coastguard Worker OpenCurrentFile();
325*d9f75844SAndroid Build Coastguard Worker OnRotation();
326*d9f75844SAndroid Build Coastguard Worker }
327*d9f75844SAndroid Build Coastguard Worker
GetFilePath(size_t index,size_t num_files) const328*d9f75844SAndroid Build Coastguard Worker std::string FileRotatingStream::GetFilePath(size_t index,
329*d9f75844SAndroid Build Coastguard Worker size_t num_files) const {
330*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_LT(index, num_files);
331*d9f75844SAndroid Build Coastguard Worker
332*d9f75844SAndroid Build Coastguard Worker const size_t buffer_size = 32;
333*d9f75844SAndroid Build Coastguard Worker char file_postfix[buffer_size];
334*d9f75844SAndroid Build Coastguard Worker // We want to zero pad the index so that it will sort nicely.
335*d9f75844SAndroid Build Coastguard Worker const int max_digits = std::snprintf(nullptr, 0, "%zu", num_files - 1);
336*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_LT(1 + max_digits, buffer_size);
337*d9f75844SAndroid Build Coastguard Worker std::snprintf(file_postfix, buffer_size, "_%0*zu", max_digits, index);
338*d9f75844SAndroid Build Coastguard Worker
339*d9f75844SAndroid Build Coastguard Worker return dir_path_ + file_prefix_ + file_postfix;
340*d9f75844SAndroid Build Coastguard Worker }
341*d9f75844SAndroid Build Coastguard Worker
CallSessionFileRotatingStream(absl::string_view dir_path,size_t max_total_log_size)342*d9f75844SAndroid Build Coastguard Worker CallSessionFileRotatingStream::CallSessionFileRotatingStream(
343*d9f75844SAndroid Build Coastguard Worker absl::string_view dir_path,
344*d9f75844SAndroid Build Coastguard Worker size_t max_total_log_size)
345*d9f75844SAndroid Build Coastguard Worker : FileRotatingStream(dir_path,
346*d9f75844SAndroid Build Coastguard Worker kCallSessionLogPrefix,
347*d9f75844SAndroid Build Coastguard Worker max_total_log_size / 2,
348*d9f75844SAndroid Build Coastguard Worker GetNumRotatingLogFiles(max_total_log_size) + 1),
349*d9f75844SAndroid Build Coastguard Worker max_total_log_size_(max_total_log_size),
350*d9f75844SAndroid Build Coastguard Worker num_rotations_(0) {
351*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GE(max_total_log_size, 4);
352*d9f75844SAndroid Build Coastguard Worker }
353*d9f75844SAndroid Build Coastguard Worker
354*d9f75844SAndroid Build Coastguard Worker const size_t CallSessionFileRotatingStream::kRotatingLogFileDefaultSize =
355*d9f75844SAndroid Build Coastguard Worker 1024 * 1024;
356*d9f75844SAndroid Build Coastguard Worker
OnRotation()357*d9f75844SAndroid Build Coastguard Worker void CallSessionFileRotatingStream::OnRotation() {
358*d9f75844SAndroid Build Coastguard Worker ++num_rotations_;
359*d9f75844SAndroid Build Coastguard Worker if (num_rotations_ == 1) {
360*d9f75844SAndroid Build Coastguard Worker // On the first rotation adjust the max file size so subsequent files after
361*d9f75844SAndroid Build Coastguard Worker // the first are smaller.
362*d9f75844SAndroid Build Coastguard Worker SetMaxFileSize(GetRotatingLogSize(max_total_log_size_));
363*d9f75844SAndroid Build Coastguard Worker } else if (num_rotations_ == (GetNumFiles() - 1)) {
364*d9f75844SAndroid Build Coastguard Worker // On the next rotation the very first file is going to be deleted. Change
365*d9f75844SAndroid Build Coastguard Worker // the rotation index so this doesn't happen.
366*d9f75844SAndroid Build Coastguard Worker SetRotationIndex(GetRotationIndex() - 1);
367*d9f75844SAndroid Build Coastguard Worker }
368*d9f75844SAndroid Build Coastguard Worker }
369*d9f75844SAndroid Build Coastguard Worker
GetRotatingLogSize(size_t max_total_log_size)370*d9f75844SAndroid Build Coastguard Worker size_t CallSessionFileRotatingStream::GetRotatingLogSize(
371*d9f75844SAndroid Build Coastguard Worker size_t max_total_log_size) {
372*d9f75844SAndroid Build Coastguard Worker size_t num_rotating_log_files = GetNumRotatingLogFiles(max_total_log_size);
373*d9f75844SAndroid Build Coastguard Worker size_t rotating_log_size = num_rotating_log_files > 2
374*d9f75844SAndroid Build Coastguard Worker ? kRotatingLogFileDefaultSize
375*d9f75844SAndroid Build Coastguard Worker : max_total_log_size / 4;
376*d9f75844SAndroid Build Coastguard Worker return rotating_log_size;
377*d9f75844SAndroid Build Coastguard Worker }
378*d9f75844SAndroid Build Coastguard Worker
GetNumRotatingLogFiles(size_t max_total_log_size)379*d9f75844SAndroid Build Coastguard Worker size_t CallSessionFileRotatingStream::GetNumRotatingLogFiles(
380*d9f75844SAndroid Build Coastguard Worker size_t max_total_log_size) {
381*d9f75844SAndroid Build Coastguard Worker // At minimum have two rotating files. Otherwise split the available log size
382*d9f75844SAndroid Build Coastguard Worker // evenly across 1MB files.
383*d9f75844SAndroid Build Coastguard Worker return std::max((size_t)2,
384*d9f75844SAndroid Build Coastguard Worker (max_total_log_size / 2) / kRotatingLogFileDefaultSize);
385*d9f75844SAndroid Build Coastguard Worker }
386*d9f75844SAndroid Build Coastguard Worker
FileRotatingStreamReader(absl::string_view dir_path,absl::string_view file_prefix)387*d9f75844SAndroid Build Coastguard Worker FileRotatingStreamReader::FileRotatingStreamReader(
388*d9f75844SAndroid Build Coastguard Worker absl::string_view dir_path,
389*d9f75844SAndroid Build Coastguard Worker absl::string_view file_prefix) {
390*d9f75844SAndroid Build Coastguard Worker file_names_ = GetFilesWithPrefix(AddTrailingPathDelimiterIfNeeded(dir_path),
391*d9f75844SAndroid Build Coastguard Worker file_prefix);
392*d9f75844SAndroid Build Coastguard Worker
393*d9f75844SAndroid Build Coastguard Worker // Plain sort of the file names would sort by age, i.e., oldest last. Using
394*d9f75844SAndroid Build Coastguard Worker // std::greater gives us the desired chronological older, oldest first.
395*d9f75844SAndroid Build Coastguard Worker absl::c_sort(file_names_, std::greater<std::string>());
396*d9f75844SAndroid Build Coastguard Worker }
397*d9f75844SAndroid Build Coastguard Worker
398*d9f75844SAndroid Build Coastguard Worker FileRotatingStreamReader::~FileRotatingStreamReader() = default;
399*d9f75844SAndroid Build Coastguard Worker
GetSize() const400*d9f75844SAndroid Build Coastguard Worker size_t FileRotatingStreamReader::GetSize() const {
401*d9f75844SAndroid Build Coastguard Worker size_t total_size = 0;
402*d9f75844SAndroid Build Coastguard Worker for (const auto& file_name : file_names_) {
403*d9f75844SAndroid Build Coastguard Worker total_size += GetFileSize(file_name).value_or(0);
404*d9f75844SAndroid Build Coastguard Worker }
405*d9f75844SAndroid Build Coastguard Worker return total_size;
406*d9f75844SAndroid Build Coastguard Worker }
407*d9f75844SAndroid Build Coastguard Worker
ReadAll(void * buffer,size_t size) const408*d9f75844SAndroid Build Coastguard Worker size_t FileRotatingStreamReader::ReadAll(void* buffer, size_t size) const {
409*d9f75844SAndroid Build Coastguard Worker size_t done = 0;
410*d9f75844SAndroid Build Coastguard Worker for (const auto& file_name : file_names_) {
411*d9f75844SAndroid Build Coastguard Worker if (done < size) {
412*d9f75844SAndroid Build Coastguard Worker webrtc::FileWrapper f = webrtc::FileWrapper::OpenReadOnly(file_name);
413*d9f75844SAndroid Build Coastguard Worker if (!f.is_open()) {
414*d9f75844SAndroid Build Coastguard Worker break;
415*d9f75844SAndroid Build Coastguard Worker }
416*d9f75844SAndroid Build Coastguard Worker done += f.Read(static_cast<char*>(buffer) + done, size - done);
417*d9f75844SAndroid Build Coastguard Worker } else {
418*d9f75844SAndroid Build Coastguard Worker break;
419*d9f75844SAndroid Build Coastguard Worker }
420*d9f75844SAndroid Build Coastguard Worker }
421*d9f75844SAndroid Build Coastguard Worker return done;
422*d9f75844SAndroid Build Coastguard Worker }
423*d9f75844SAndroid Build Coastguard Worker
CallSessionFileRotatingStreamReader(absl::string_view dir_path)424*d9f75844SAndroid Build Coastguard Worker CallSessionFileRotatingStreamReader::CallSessionFileRotatingStreamReader(
425*d9f75844SAndroid Build Coastguard Worker absl::string_view dir_path)
426*d9f75844SAndroid Build Coastguard Worker : FileRotatingStreamReader(dir_path, kCallSessionLogPrefix) {}
427*d9f75844SAndroid Build Coastguard Worker
428*d9f75844SAndroid Build Coastguard Worker } // namespace rtc
429