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#include <cstdlib> 24#include <string> 25#include <utility> 26#include <android-base/file.h> 27#include <android-base/logging.h> 28#include <android-base/parseint.h> 29#include "uptime_parser.h" 30 31namespace { 32 33const char BOOTSTAT_DATA_DIR[] = "/data/misc/bootstat/"; 34 35// Given a boot even record file at |path|, extracts the event's relative time 36// from the record into |uptime|. 37bool ParseRecordEventTime(const std::string& path, int32_t* uptime) { 38 DCHECK_NE(static_cast<int32_t*>(nullptr), uptime); 39 40 struct stat file_stat; 41 if (stat(path.c_str(), &file_stat) == -1) { 42 PLOG(ERROR) << "Failed to read " << path; 43 return false; 44 } 45 46 *uptime = file_stat.st_mtime; 47 48 return true; 49} 50 51} // namespace 52 53BootEventRecordStore::BootEventRecordStore() { 54 SetStorePath(BOOTSTAT_DATA_DIR); 55} 56 57void BootEventRecordStore::AddBootEvent(const std::string& event) { 58 AddBootEventWithValue(event, bootstat::ParseUptime()); 59} 60 61// The implementation of AddBootEventValue makes use of the mtime file 62// attribute to store the value associated with a boot event in order to 63// optimize on-disk size requirements and small-file thrashing. 64void BootEventRecordStore::AddBootEventWithValue( 65 const std::string& event, int32_t value) { 66 std::string record_path = GetBootEventPath(event); 67 int record_fd = creat(record_path.c_str(), S_IRUSR | S_IWUSR); 68 if (record_fd == -1) { 69 PLOG(ERROR) << "Failed to create " << record_path; 70 return; 71 } 72 73 // Fill out the stat structure for |record_path| in order to get the atime to 74 // set in the utime() call. 75 struct stat file_stat; 76 if (stat(record_path.c_str(), &file_stat) == -1) { 77 PLOG(ERROR) << "Failed to read " << record_path; 78 close(record_fd); 79 return; 80 } 81 82 // Set the |modtime| of the file to store the value of the boot event while 83 // preserving the |actime| (as read by stat). 84 struct utimbuf times = {/* actime */ file_stat.st_atime, /* modtime */ value}; 85 if (utime(record_path.c_str(), ×) == -1) { 86 PLOG(ERROR) << "Failed to set mtime for " << record_path; 87 close(record_fd); 88 return; 89 } 90 91 close(record_fd); 92} 93 94bool BootEventRecordStore::GetBootEvent( 95 const std::string& event, BootEventRecord* record) const { 96 CHECK_NE(static_cast<BootEventRecord*>(nullptr), record); 97 CHECK(!event.empty()); 98 99 const std::string record_path = GetBootEventPath(event); 100 int32_t uptime; 101 if (!ParseRecordEventTime(record_path, &uptime)) { 102 LOG(ERROR) << "Failed to parse boot time record: " << record_path; 103 return false; 104 } 105 106 *record = std::make_pair(event, uptime); 107 return true; 108} 109 110std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore:: 111 GetAllBootEvents() const { 112 std::vector<BootEventRecord> events; 113 114 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(store_path_.c_str()), closedir); 115 116 // This case could happen due to external manipulation of the filesystem, 117 // so crash out if the record store doesn't exist. 118 CHECK_NE(static_cast<DIR*>(nullptr), dir.get()); 119 120 struct dirent* entry; 121 while ((entry = readdir(dir.get())) != NULL) { 122 // Only parse regular files. 123 if (entry->d_type != DT_REG) { 124 continue; 125 } 126 127 const std::string event = entry->d_name; 128 BootEventRecord record; 129 if (!GetBootEvent(event, &record)) { 130 LOG(ERROR) << "Failed to parse boot time event: " << event; 131 continue; 132 } 133 134 events.push_back(record); 135 } 136 137 return events; 138} 139 140void BootEventRecordStore::SetStorePath(const std::string& path) { 141 DCHECK_EQ('/', path.back()); 142 store_path_ = path; 143} 144 145std::string BootEventRecordStore::GetBootEventPath( 146 const std::string& event) const { 147 DCHECK_EQ('/', store_path_.back()); 148 return store_path_ + event; 149} 150