1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "boot_event_record_store.h" 18 19#include <dirent.h> 20#include <fcntl.h> 21#include <sys/stat.h> 22#include <utime.h> 23 24#include <chrono> 25#include <cstdlib> 26#include <string> 27#include <utility> 28 29#include <android-base/chrono_utils.h> 30#include <android-base/file.h> 31#include <android-base/logging.h> 32#include <android-base/parseint.h> 33 34namespace { 35 36const char BOOTSTAT_DATA_DIR[] = "/data/misc/bootstat/"; 37 38// Given a boot even record file at |path|, extracts the event's relative time 39// from the record into |uptime|. 40bool ParseRecordEventTime(const std::string& path, int32_t* uptime) { 41 DCHECK_NE(static_cast<int32_t*>(nullptr), uptime); 42 43 struct stat file_stat; 44 if (stat(path.c_str(), &file_stat) == -1) { 45 PLOG(ERROR) << "Failed to read " << path; 46 return false; 47 } 48 49 *uptime = file_stat.st_mtime; 50 51 return true; 52} 53 54} // namespace 55 56BootEventRecordStore::BootEventRecordStore() { 57 SetStorePath(BOOTSTAT_DATA_DIR); 58} 59 60void BootEventRecordStore::AddBootEvent(const std::string& event) { 61 auto uptime = std::chrono::duration_cast<std::chrono::seconds>( 62 android::base::boot_clock::now().time_since_epoch()); 63 AddBootEventWithValue(event, uptime.count()); 64} 65 66// The implementation of AddBootEventValue makes use of the mtime file 67// attribute to store the value associated with a boot event in order to 68// optimize on-disk size requirements and small-file thrashing. 69void BootEventRecordStore::AddBootEventWithValue(const std::string& event, int32_t value) { 70 std::string record_path = GetBootEventPath(event); 71 int record_fd = creat(record_path.c_str(), S_IRUSR | S_IWUSR); 72 if (record_fd == -1) { 73 PLOG(ERROR) << "Failed to create " << record_path; 74 return; 75 } 76 77 // Fill out the stat structure for |record_path| in order to get the atime to 78 // set in the utime() call. 79 struct stat file_stat; 80 if (stat(record_path.c_str(), &file_stat) == -1) { 81 PLOG(ERROR) << "Failed to read " << record_path; 82 close(record_fd); 83 return; 84 } 85 86 // Set the |modtime| of the file to store the value of the boot event while 87 // preserving the |actime| (as read by stat). 88 struct utimbuf times = {/* actime */ file_stat.st_atime, /* modtime */ value}; 89 if (utime(record_path.c_str(), ×) == -1) { 90 PLOG(ERROR) << "Failed to set mtime for " << record_path; 91 close(record_fd); 92 return; 93 } 94 95 close(record_fd); 96} 97 98bool BootEventRecordStore::GetBootEvent(const std::string& event, BootEventRecord* record) const { 99 CHECK_NE(static_cast<BootEventRecord*>(nullptr), record); 100 CHECK(!event.empty()); 101 102 const std::string record_path = GetBootEventPath(event); 103 int32_t uptime; 104 if (!ParseRecordEventTime(record_path, &uptime)) { 105 LOG(ERROR) << "Failed to parse boot time record: " << record_path; 106 return false; 107 } 108 109 *record = std::make_pair(event, uptime); 110 return true; 111} 112 113std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore::GetAllBootEvents() const { 114 std::vector<BootEventRecord> events; 115 116 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(store_path_.c_str()), closedir); 117 118 // This case could happen due to external manipulation of the filesystem, 119 // so crash out if the record store doesn't exist. 120 CHECK_NE(static_cast<DIR*>(nullptr), dir.get()); 121 122 struct dirent* entry; 123 while ((entry = readdir(dir.get())) != NULL) { 124 // Only parse regular files. 125 if (entry->d_type != DT_REG) { 126 continue; 127 } 128 129 const std::string event = entry->d_name; 130 BootEventRecord record; 131 if (!GetBootEvent(event, &record)) { 132 LOG(ERROR) << "Failed to parse boot time event: " << event; 133 continue; 134 } 135 136 events.push_back(record); 137 } 138 139 return events; 140} 141 142void BootEventRecordStore::SetStorePath(const std::string& path) { 143 DCHECK_EQ('/', path.back()); 144 store_path_ = path; 145} 146 147std::string BootEventRecordStore::GetBootEventPath(const std::string& event) const { 148 DCHECK_EQ('/', store_path_.back()); 149 return store_path_ + event; 150} 151