OLD | NEW |
(Empty) | |
| 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. |
| 4 |
| 5 #include "chromecast/crash/linux/synchronized_minidump_manager.h" |
| 6 |
| 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> |
| 16 |
| 17 #include <fstream> |
| 18 |
| 19 #include "base/logging.h" |
| 20 #include "chromecast/base/path_utils.h" |
| 21 #include "chromecast/crash/linux/dump_info.h" |
| 22 |
| 23 namespace chromecast { |
| 24 |
| 25 namespace { |
| 26 |
| 27 const mode_t kDirMode = 0770; |
| 28 const mode_t kFileMode = 0660; |
| 29 const char kLockfileName[] = "lockfile"; |
| 30 const char kMinidumpsDir[] = "minidumps"; |
| 31 |
| 32 } // namespace |
| 33 |
| 34 SynchronizedMinidumpManager::SynchronizedMinidumpManager() |
| 35 : non_blocking_(false), lockfile_fd_(-1) { |
| 36 dump_path_ = GetHomePathASCII(kMinidumpsDir); |
| 37 lockfile_path_ = dump_path_.Append(kLockfileName).value(); |
| 38 } |
| 39 |
| 40 SynchronizedMinidumpManager::~SynchronizedMinidumpManager() { |
| 41 // Release the lock if held. |
| 42 ReleaseLockFile(); |
| 43 } |
| 44 |
| 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; |
| 50 |
| 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; |
| 56 } |
| 57 |
| 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; |
| 65 } |
| 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); |
| 74 } |
| 75 } |
| 76 } |
| 77 } |
| 78 |
| 79 closedir(dirp); |
| 80 return num_dumps; |
| 81 } |
| 82 |
| 83 int SynchronizedMinidumpManager::AcquireLockAndDoWork() { |
| 84 int success = -1; |
| 85 if (AcquireLockFile() >= 0) { |
| 86 success = DoWork(); |
| 87 ReleaseLockFile(); |
| 88 } |
| 89 return success; |
| 90 } |
| 91 |
| 92 const ScopedVector<DumpInfo>& SynchronizedMinidumpManager::GetDumpMetadata() { |
| 93 DCHECK_GE(lockfile_fd_, 0); |
| 94 if (!dump_metadata_) |
| 95 ParseLockFile(); |
| 96 return *dump_metadata_; |
| 97 } |
| 98 |
| 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; |
| 106 } |
| 107 |
| 108 // Open the lockfile. Create it if it does not exist. |
| 109 lockfile_fd_ = open(lockfile_path_.c_str(), O_RDWR | O_CREAT, kFileMode); |
| 110 |
| 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; |
| 116 } |
| 117 |
| 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; |
| 125 } |
| 126 |
| 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; |
| 132 } |
| 133 |
| 134 // We successfully have acquired the lock. |
| 135 return 0; |
| 136 } |
| 137 |
| 138 int SynchronizedMinidumpManager::ParseLockFile() { |
| 139 DCHECK_GE(lockfile_fd_, 0); |
| 140 DCHECK(!dump_metadata_); |
| 141 |
| 142 scoped_ptr<ScopedVector<DumpInfo> > dumps(new ScopedVector<DumpInfo>()); |
| 143 std::string entry; |
| 144 |
| 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; |
| 151 } |
| 152 |
| 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; |
| 161 } |
| 162 } |
| 163 |
| 164 dump_metadata_ = dumps.Pass(); |
| 165 return 0; |
| 166 } |
| 167 |
| 168 int SynchronizedMinidumpManager::AddEntryToLockFile(const DumpInfo& dump_info) { |
| 169 DCHECK_LE(0, lockfile_fd_); |
| 170 |
| 171 // Make sure dump_info is valid. |
| 172 if (!dump_info.valid()) { |
| 173 LOG(ERROR) << "Entry to be added is invalid"; |
| 174 return -1; |
| 175 } |
| 176 |
| 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; |
| 182 } |
| 183 |
| 184 // Write the string and close the file. |
| 185 out << dump_info.entry(); |
| 186 out.close(); |
| 187 return 0; |
| 188 } |
| 189 |
| 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; |
| 194 |
| 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(); |
| 200 } |
| 201 out.close(); |
| 202 return 0; |
| 203 } |
| 204 |
| 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_); |
| 211 |
| 212 // We may use this object again, so we should reset this. |
| 213 lockfile_fd_ = -1; |
| 214 } |
| 215 |
| 216 dump_metadata_.reset(); |
| 217 } |
| 218 |
| 219 } // namespace chromecast |
OLD | NEW |