1 /* 2 * Copyright (C) 2019 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 "apexd_session.h" 18 19 #include "apexd_utils.h" 20 #include "string_log.h" 21 22 #include "session_state.pb.h" 23 24 #include <android-base/logging.h> 25 #include <android-base/stringprintf.h> 26 #include <dirent.h> 27 #include <sys/stat.h> 28 29 #include <filesystem> 30 #include <fstream> 31 #include <optional> 32 #include <utility> 33 34 using android::base::Error; 35 using android::base::Result; 36 using android::base::StringPrintf; 37 using apex::proto::SessionState; 38 39 namespace android { 40 namespace apex { 41 42 namespace { 43 44 // Starting from R, apexd prefers /metadata partition (kNewApexSessionsDir) as 45 // location for sessions-related information. For devices that don't have 46 // /metadata partition, apexd will fallback to the /data one 47 // (kOldApexSessionsDir). 48 static constexpr const char* kOldApexSessionsDir = "/data/apex/sessions"; 49 static constexpr const char* kNewApexSessionsDir = "/metadata/apex/sessions"; 50 51 static constexpr const char* kStateFileName = "state"; 52 53 } // namespace 54 55 ApexSession::ApexSession(SessionState state) : state_(std::move(state)) {} 56 57 std::string ApexSession::GetSessionsDir() { 58 static std::string result; 59 static std::once_flag once_flag; 60 std::call_once(once_flag, [&]() { 61 auto status = 62 FindFirstExistingDirectory(kNewApexSessionsDir, kOldApexSessionsDir); 63 if (!status.ok()) { 64 LOG(FATAL) << status.error(); 65 } 66 result = std::move(*status); 67 }); 68 return result; 69 } 70 71 Result<void> ApexSession::MigrateToMetadataSessionsDir() { 72 return MoveDir(kOldApexSessionsDir, kNewApexSessionsDir); 73 } 74 75 Result<ApexSession> ApexSession::CreateSession(int session_id) { 76 SessionState state; 77 // Create session directory 78 std::string session_dir = GetSessionsDir() + "/" + std::to_string(session_id); 79 if (auto status = CreateDirIfNeeded(session_dir, 0700); !status.ok()) { 80 return status.error(); 81 } 82 state.set_id(session_id); 83 84 return ApexSession(state); 85 } 86 87 Result<ApexSession> ApexSession::GetSessionFromFile(const std::string& path) { 88 SessionState state; 89 std::fstream state_file(path, std::ios::in | std::ios::binary); 90 if (!state_file) { 91 return Error() << "Failed to open " << path; 92 } 93 94 if (!state.ParseFromIstream(&state_file)) { 95 return Error() << "Failed to parse " << path; 96 } 97 98 return ApexSession(state); 99 } 100 101 Result<ApexSession> ApexSession::GetSession(int session_id) { 102 auto path = StringPrintf("%s/%d/%s", GetSessionsDir().c_str(), session_id, 103 kStateFileName); 104 105 return GetSessionFromFile(path); 106 } 107 108 std::vector<ApexSession> ApexSession::GetSessions() { 109 std::vector<ApexSession> sessions; 110 111 Result<std::vector<std::string>> session_paths = ReadDir( 112 GetSessionsDir(), [](const std::filesystem::directory_entry& entry) { 113 std::error_code ec; 114 return entry.is_directory(ec); 115 }); 116 117 if (!session_paths.ok()) { 118 return sessions; 119 } 120 121 for (const std::string& session_dir_path : *session_paths) { 122 // Try to read session state 123 auto session = GetSessionFromFile(session_dir_path + "/" + kStateFileName); 124 if (!session.ok()) { 125 LOG(WARNING) << session.error(); 126 continue; 127 } 128 sessions.push_back(std::move(*session)); 129 } 130 131 return sessions; 132 } 133 134 std::vector<ApexSession> ApexSession::GetSessionsInState( 135 SessionState::State state) { 136 auto sessions = GetSessions(); 137 sessions.erase( 138 std::remove_if(sessions.begin(), sessions.end(), 139 [&](const ApexSession &s) { return s.GetState() != state; }), 140 sessions.end()); 141 142 return sessions; 143 } 144 145 std::vector<ApexSession> ApexSession::GetActiveSessions() { 146 auto sessions = GetSessions(); 147 std::vector<ApexSession> active_sessions; 148 for (const ApexSession& session : sessions) { 149 if (!session.IsFinalized() && session.GetState() != SessionState::UNKNOWN) { 150 active_sessions.push_back(session); 151 } 152 } 153 return active_sessions; 154 } 155 156 SessionState::State ApexSession::GetState() const { return state_.state(); } 157 158 int ApexSession::GetId() const { return state_.id(); } 159 160 const std::string& ApexSession::GetBuildFingerprint() const { 161 return state_.expected_build_fingerprint(); 162 } 163 164 bool ApexSession::IsFinalized() const { 165 switch (GetState()) { 166 case SessionState::SUCCESS: 167 case SessionState::ACTIVATION_FAILED: 168 case SessionState::REVERTED: 169 case SessionState::REVERT_FAILED: 170 return true; 171 default: 172 return false; 173 } 174 } 175 176 bool ApexSession::HasRollbackEnabled() const { 177 return state_.rollback_enabled(); 178 } 179 180 bool ApexSession::IsRollback() const { return state_.is_rollback(); } 181 182 int ApexSession::GetRollbackId() const { return state_.rollback_id(); } 183 184 const std::string& ApexSession::GetCrashingNativeProcess() const { 185 return state_.crashing_native_process(); 186 } 187 188 const std::string& ApexSession::GetErrorMessage() const { 189 return state_.error_message(); 190 } 191 192 const google::protobuf::RepeatedField<int> ApexSession::GetChildSessionIds() 193 const { 194 return state_.child_session_ids(); 195 } 196 197 void ApexSession::SetChildSessionIds( 198 const std::vector<int>& child_session_ids) { 199 *(state_.mutable_child_session_ids()) = {child_session_ids.begin(), 200 child_session_ids.end()}; 201 } 202 203 const google::protobuf::RepeatedPtrField<std::string> 204 ApexSession::GetApexNames() const { 205 return state_.apex_names(); 206 } 207 208 void ApexSession::SetBuildFingerprint(const std::string& fingerprint) { 209 *(state_.mutable_expected_build_fingerprint()) = fingerprint; 210 } 211 212 void ApexSession::SetHasRollbackEnabled(const bool enabled) { 213 state_.set_rollback_enabled(enabled); 214 } 215 216 void ApexSession::SetIsRollback(const bool is_rollback) { 217 state_.set_is_rollback(is_rollback); 218 } 219 220 void ApexSession::SetRollbackId(const int rollback_id) { 221 state_.set_rollback_id(rollback_id); 222 } 223 224 void ApexSession::SetCrashingNativeProcess( 225 const std::string& crashing_process) { 226 state_.set_crashing_native_process(crashing_process); 227 } 228 229 void ApexSession::SetErrorMessage(const std::string& error_message) { 230 state_.set_error_message(error_message); 231 } 232 233 void ApexSession::AddApexName(const std::string& apex_name) { 234 state_.add_apex_names(apex_name); 235 } 236 237 Result<void> ApexSession::UpdateStateAndCommit( 238 const SessionState::State& session_state) { 239 state_.set_state(session_state); 240 241 auto state_file_path = StringPrintf("%s/%d/%s", GetSessionsDir().c_str(), 242 state_.id(), kStateFileName); 243 244 std::fstream state_file(state_file_path, 245 std::ios::out | std::ios::trunc | std::ios::binary); 246 if (!state_.SerializeToOstream(&state_file)) { 247 return Error() << "Failed to write state file " << state_file_path; 248 } 249 250 return {}; 251 } 252 253 Result<void> ApexSession::DeleteSession() const { 254 std::string session_dir = GetSessionsDir() + "/" + std::to_string(GetId()); 255 LOG(INFO) << "Deleting " << session_dir; 256 auto path = std::filesystem::path(session_dir); 257 std::error_code error_code; 258 std::filesystem::remove_all(path, error_code); 259 if (error_code) { 260 return Error() << "Failed to delete " << session_dir << " : " 261 << error_code.message(); 262 } 263 return {}; 264 } 265 266 std::ostream& operator<<(std::ostream& out, const ApexSession& session) { 267 return out << "[id = " << session.GetId() 268 << "; state = " << SessionState::State_Name(session.GetState()) 269 << "]"; 270 } 271 272 void ApexSession::DeleteFinalizedSessions() { 273 auto sessions = GetSessions(); 274 for (const ApexSession& session : sessions) { 275 if (!session.IsFinalized()) { 276 continue; 277 } 278 auto result = session.DeleteSession(); 279 if (!result.ok()) { 280 LOG(WARNING) << "Failed to delete finalized session: " << session.GetId(); 281 } 282 } 283 } 284 285 } // namespace apex 286 } // namespace android 287