Update UnusedResources lint suppressions.
[chromium-blink-merge.git] / chromecast / crash / linux / synchronized_minidump_manager.cc
blob358427a2dd31a3532e82a9d6703bd78f00f161e2
1 // Copyright 2015 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.
5 #include "chromecast/crash/linux/synchronized_minidump_manager.h"
7 #include <dirent.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <grp.h>
11 #include <string.h>
12 #include <sys/file.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <unistd.h>
17 #include <fstream>
19 #include "base/logging.h"
20 #include "chromecast/base/path_utils.h"
21 #include "chromecast/crash/linux/dump_info.h"
23 namespace chromecast {
25 namespace {
27 const mode_t kDirMode = 0770;
28 const mode_t kFileMode = 0660;
29 const char kLockfileName[] = "lockfile";
30 const char kMinidumpsDir[] = "minidumps";
32 } // namespace
34 SynchronizedMinidumpManager::SynchronizedMinidumpManager()
35 : non_blocking_(false), lockfile_fd_(-1) {
36 dump_path_ = GetHomePathASCII(kMinidumpsDir);
37 lockfile_path_ = dump_path_.Append(kLockfileName).value();
40 SynchronizedMinidumpManager::~SynchronizedMinidumpManager() {
41 // Release the lock if held.
42 ReleaseLockFile();
45 // TODO(slan): Move some of this pruning logic to ReleaseLockFile?
46 int SynchronizedMinidumpManager::GetNumDumps(bool delete_all_dumps) {
47 DIR* dirp;
48 struct dirent* dptr;
49 int num_dumps = 0;
51 // folder does not exist
52 dirp = opendir(dump_path_.value().c_str());
53 if (dirp == NULL) {
54 LOG(ERROR) << "Unable to open directory " << dump_path_.value();
55 return 0;
58 while ((dptr = readdir(dirp)) != NULL) {
59 struct stat buf;
60 const std::string file_fullname = dump_path_.value() + "/" + dptr->d_name;
61 if (lstat(file_fullname.c_str(), &buf) == -1 || !S_ISREG(buf.st_mode)) {
62 // if we cannot lstat this file, it is probably bad, so skip
63 // if the file is not regular, skip
64 continue;
66 // 'lockfile' is not counted
67 if (lockfile_path_ != file_fullname) {
68 ++num_dumps;
69 if (delete_all_dumps) {
70 LOG(INFO) << "Removing " << dptr->d_name
71 << "which was not in the lockfile";
72 if (remove(file_fullname.c_str()) < 0) {
73 LOG(INFO) << "remove failed. error " << strerror(errno);
79 closedir(dirp);
80 return num_dumps;
83 int SynchronizedMinidumpManager::AcquireLockAndDoWork() {
84 int success = -1;
85 if (AcquireLockFile() >= 0) {
86 success = DoWork();
87 ReleaseLockFile();
89 return success;
92 const ScopedVector<DumpInfo>& SynchronizedMinidumpManager::GetDumpMetadata() {
93 DCHECK_GE(lockfile_fd_, 0);
94 if (!dump_metadata_)
95 ParseLockFile();
96 return *dump_metadata_;
99 int SynchronizedMinidumpManager::AcquireLockFile() {
100 DCHECK_LT(lockfile_fd_, 0);
101 // Make the directory for the minidumps if it does not exist.
102 if (mkdir(dump_path_.value().c_str(), kDirMode) < 0 && errno != EEXIST) {
103 LOG(ERROR) << "mkdir for " << dump_path_.value().c_str()
104 << " failed. error = " << strerror(errno);
105 return -1;
108 // Open the lockfile. Create it if it does not exist.
109 lockfile_fd_ = open(lockfile_path_.c_str(), O_RDWR | O_CREAT, kFileMode);
111 // If opening or creating the lockfile failed, we don't want to proceed
112 // with dump writing for fear of exhausting up system resources.
113 if (lockfile_fd_ < 0) {
114 LOG(ERROR) << "open lockfile failed " << lockfile_path_;
115 return -1;
118 // Acquire the lock on the file. Whether or not we are in non-blocking mode,
119 // flock failure means that we did not acquire it and this method should fail.
120 int operation_mode = non_blocking_ ? (LOCK_EX | LOCK_NB) : LOCK_EX;
121 if (flock(lockfile_fd_, operation_mode) < 0) {
122 ReleaseLockFile();
123 LOG(INFO) << "flock lockfile failed, error = " << strerror(errno);
124 return -1;
127 // The lockfile is open and locked. Parse it to provide subclasses with a
128 // record of all the current dumps.
129 if (ParseLockFile() < 0) {
130 LOG(ERROR) << "Lockfile did not parse correctly. ";
131 return -1;
134 // We successfully have acquired the lock.
135 return 0;
138 int SynchronizedMinidumpManager::ParseLockFile() {
139 DCHECK_GE(lockfile_fd_, 0);
140 DCHECK(!dump_metadata_);
142 scoped_ptr<ScopedVector<DumpInfo> > dumps(new ScopedVector<DumpInfo>());
143 std::string entry;
145 // Instead of using |lockfile_fd_|, use <fstream> for readability.
146 std::ifstream in(lockfile_path_);
147 if (!in.is_open()) {
148 NOTREACHED();
149 LOG(ERROR) << lockfile_path_ << " could not be opened.";
150 return -1;
153 // Grab each entry.
154 while (std::getline(in, entry)) {
155 scoped_ptr<DumpInfo> info(new DumpInfo(entry));
156 if (info->valid() && info->crashed_process_dump().size() > 0) {
157 dumps->push_back(info.Pass());
158 } else {
159 LOG(WARNING) << "Entry is not valid: " << entry;
160 return -1;
164 dump_metadata_ = dumps.Pass();
165 return 0;
168 int SynchronizedMinidumpManager::AddEntryToLockFile(const DumpInfo& dump_info) {
169 DCHECK_LE(0, lockfile_fd_);
171 // Make sure dump_info is valid.
172 if (!dump_info.valid()) {
173 LOG(ERROR) << "Entry to be added is invalid";
174 return -1;
177 // Open the file.
178 std::ofstream out(lockfile_path_, std::ios::app);
179 if (!out.is_open()) {
180 NOTREACHED() << "Lockfile would not open.";
181 return -1;
184 // Write the string and close the file.
185 out << dump_info.entry();
186 out.close();
187 return 0;
190 int SynchronizedMinidumpManager::RemoveEntryFromLockFile(int index) {
191 const auto& entries = GetDumpMetadata();
192 if (index < 0 || static_cast<size_t>(index) >= entries.size())
193 return -1;
195 // Remove the entry and write all remaining entries to file.
196 dump_metadata_->erase(dump_metadata_->begin() + index);
197 std::ofstream out(lockfile_path_);
198 for (auto info : *dump_metadata_) {
199 out << info->entry();
201 out.close();
202 return 0;
205 void SynchronizedMinidumpManager::ReleaseLockFile() {
206 // flock is associated with the fd entry in the open fd table, so closing
207 // all fd's will release the lock. To be safe, we explicitly unlock.
208 if (lockfile_fd_ >= 0) {
209 flock(lockfile_fd_, LOCK_UN);
210 close(lockfile_fd_);
212 // We may use this object again, so we should reset this.
213 lockfile_fd_ = -1;
216 dump_metadata_.reset();
219 } // namespace chromecast