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"
14 #include <sys/types.h>
19 #include "base/logging.h"
20 #include "chromecast/base/path_utils.h"
21 #include "chromecast/crash/linux/dump_info.h"
23 namespace chromecast
{
27 const mode_t kDirMode
= 0770;
28 const mode_t kFileMode
= 0660;
29 const char kLockfileName
[] = "lockfile";
30 const char kMinidumpsDir
[] = "minidumps";
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.
45 // TODO(slan): Move some of this pruning logic to ReleaseLockFile?
46 int SynchronizedMinidumpManager::GetNumDumps(bool delete_all_dumps
) {
51 // folder does not exist
52 dirp
= opendir(dump_path_
.value().c_str());
54 LOG(ERROR
) << "Unable to open directory " << dump_path_
.value();
58 while ((dptr
= readdir(dirp
)) != NULL
) {
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
66 // 'lockfile' is not counted
67 if (lockfile_path_
!= file_fullname
) {
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
);
83 int SynchronizedMinidumpManager::AcquireLockAndDoWork() {
85 if (AcquireLockFile() >= 0) {
92 const ScopedVector
<DumpInfo
>& SynchronizedMinidumpManager::GetDumpMetadata() {
93 DCHECK_GE(lockfile_fd_
, 0);
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
);
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_
;
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) {
123 LOG(INFO
) << "flock lockfile failed, error = " << strerror(errno
);
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. ";
134 // We successfully have acquired the lock.
138 int SynchronizedMinidumpManager::ParseLockFile() {
139 DCHECK_GE(lockfile_fd_
, 0);
140 DCHECK(!dump_metadata_
);
142 scoped_ptr
<ScopedVector
<DumpInfo
> > dumps(new ScopedVector
<DumpInfo
>());
145 // Instead of using |lockfile_fd_|, use <fstream> for readability.
146 std::ifstream
in(lockfile_path_
);
149 LOG(ERROR
) << lockfile_path_
<< " could not be opened.";
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());
159 LOG(WARNING
) << "Entry is not valid: " << entry
;
164 dump_metadata_
= dumps
.Pass();
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";
178 std::ofstream
out(lockfile_path_
, std::ios::app
);
179 if (!out
.is_open()) {
180 NOTREACHED() << "Lockfile would not open.";
184 // Write the string and close the file.
185 out
<< dump_info
.entry();
190 int SynchronizedMinidumpManager::RemoveEntryFromLockFile(int index
) {
191 const auto& entries
= GetDumpMetadata();
192 if (index
< 0 || static_cast<size_t>(index
) >= entries
.size())
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();
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
);
212 // We may use this object again, so we should reset this.
216 dump_metadata_
.reset();
219 } // namespace chromecast