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 <dirent.h>
26 #include <sys/stat.h>
27
28 #include <filesystem>
29 #include <fstream>
30 #include <optional>
31 #include <utility>
32
33 using android::base::Error;
34 using android::base::Result;
35 using apex::proto::SessionState;
36
37 namespace android {
38 namespace apex {
39
40 namespace {
41
42 static constexpr const char* kStateFileName = "state";
43
getSessionDir(int session_id)44 std::string getSessionDir(int session_id) {
45 return kApexSessionsDir + "/" + std::to_string(session_id);
46 }
47
getSessionStateFilePath(int session_id)48 std::string getSessionStateFilePath(int session_id) {
49 return getSessionDir(session_id) + "/" + kStateFileName;
50 }
51
createSessionDirIfNeeded(int session_id)52 Result<std::string> createSessionDirIfNeeded(int session_id) {
53 // create /data/sessions
54 auto res = createDirIfNeeded(kApexSessionsDir, 0700);
55 if (!res.ok()) {
56 return res.error();
57 }
58 // create /data/sessions/session_id
59 std::string sessionDir = getSessionDir(session_id);
60 res = createDirIfNeeded(sessionDir, 0700);
61 if (!res.ok()) {
62 return res.error();
63 }
64
65 return sessionDir;
66 }
67
deleteSessionDir(int session_id)68 Result<void> deleteSessionDir(int session_id) {
69 std::string session_dir = getSessionDir(session_id);
70 LOG(DEBUG) << "Deleting " << session_dir;
71 auto path = std::filesystem::path(session_dir);
72 std::error_code error_code;
73 std::filesystem::remove_all(path, error_code);
74 if (error_code) {
75 return Error() << "Failed to delete " << session_dir << " : "
76 << error_code.message();
77 }
78 return {};
79 }
80
81 } // namespace
82
ApexSession(SessionState state)83 ApexSession::ApexSession(SessionState state) : state_(std::move(state)) {}
84
CreateSession(int session_id)85 Result<ApexSession> ApexSession::CreateSession(int session_id) {
86 SessionState state;
87 // Create session directory
88 auto sessionPath = createSessionDirIfNeeded(session_id);
89 if (!sessionPath.ok()) {
90 return sessionPath.error();
91 }
92 state.set_id(session_id);
93
94 return ApexSession(state);
95 }
GetSessionFromFile(const std::string & path)96 Result<ApexSession> ApexSession::GetSessionFromFile(const std::string& path) {
97 SessionState state;
98 std::fstream stateFile(path, std::ios::in | std::ios::binary);
99 if (!stateFile) {
100 return Error() << "Failed to open " << path;
101 }
102
103 if (!state.ParseFromIstream(&stateFile)) {
104 return Error() << "Failed to parse " << path;
105 }
106
107 return ApexSession(state);
108 }
109
GetSession(int session_id)110 Result<ApexSession> ApexSession::GetSession(int session_id) {
111 auto path = getSessionStateFilePath(session_id);
112
113 return GetSessionFromFile(path);
114 }
115
GetSessions()116 std::vector<ApexSession> ApexSession::GetSessions() {
117 std::vector<ApexSession> sessions;
118
119 Result<std::vector<std::string>> sessionPaths = ReadDir(
120 kApexSessionsDir, [](const std::filesystem::directory_entry& entry) {
121 std::error_code ec;
122 return entry.is_directory(ec);
123 });
124
125 if (!sessionPaths.ok()) {
126 return sessions;
127 }
128
129 for (const std::string& sessionDirPath : *sessionPaths) {
130 // Try to read session state
131 auto session = GetSessionFromFile(sessionDirPath + "/" + kStateFileName);
132 if (!session.ok()) {
133 LOG(WARNING) << session.error();
134 continue;
135 }
136 sessions.push_back(std::move(*session));
137 }
138
139 return sessions;
140 }
141
GetSessionsInState(SessionState::State state)142 std::vector<ApexSession> ApexSession::GetSessionsInState(
143 SessionState::State state) {
144 auto sessions = GetSessions();
145 sessions.erase(
146 std::remove_if(sessions.begin(), sessions.end(),
147 [&](const ApexSession &s) { return s.GetState() != state; }),
148 sessions.end());
149
150 return sessions;
151 }
152
GetActiveSessions()153 std::vector<ApexSession> ApexSession::GetActiveSessions() {
154 auto sessions = GetSessions();
155 std::vector<ApexSession> activeSessions;
156 for (const ApexSession& session : sessions) {
157 if (!session.IsFinalized() && session.GetState() != SessionState::UNKNOWN) {
158 activeSessions.push_back(session);
159 }
160 }
161 return activeSessions;
162 }
163
GetState() const164 SessionState::State ApexSession::GetState() const { return state_.state(); }
165
GetId() const166 int ApexSession::GetId() const { return state_.id(); }
167
GetBuildFingerprint() const168 std::string ApexSession::GetBuildFingerprint() const {
169 return state_.expected_build_fingerprint();
170 }
171
IsFinalized() const172 bool ApexSession::IsFinalized() const {
173 switch (GetState()) {
174 case SessionState::SUCCESS:
175 case SessionState::ACTIVATION_FAILED:
176 case SessionState::REVERTED:
177 case SessionState::REVERT_FAILED:
178 return true;
179 default:
180 return false;
181 }
182 }
183
HasRollbackEnabled() const184 bool ApexSession::HasRollbackEnabled() const {
185 return state_.rollback_enabled();
186 }
187
IsRollback() const188 bool ApexSession::IsRollback() const { return state_.is_rollback(); }
189
GetRollbackId() const190 int ApexSession::GetRollbackId() const { return state_.rollback_id(); }
191
GetCrashingNativeProcess() const192 std::string ApexSession::GetCrashingNativeProcess() const {
193 return state_.crashing_native_process();
194 }
195
GetChildSessionIds() const196 const google::protobuf::RepeatedField<int> ApexSession::GetChildSessionIds()
197 const {
198 return state_.child_session_ids();
199 }
200
SetChildSessionIds(const std::vector<int> & child_session_ids)201 void ApexSession::SetChildSessionIds(
202 const std::vector<int>& child_session_ids) {
203 *(state_.mutable_child_session_ids()) = {child_session_ids.begin(),
204 child_session_ids.end()};
205 }
206
207 const google::protobuf::RepeatedPtrField<std::string>
GetApexNames() const208 ApexSession::GetApexNames() const {
209 return state_.apex_names();
210 }
211
SetBuildFingerprint(const std::string & fingerprint)212 void ApexSession::SetBuildFingerprint(const std::string& fingerprint) {
213 *(state_.mutable_expected_build_fingerprint()) = fingerprint;
214 }
215
SetHasRollbackEnabled(const bool enabled)216 void ApexSession::SetHasRollbackEnabled(const bool enabled) {
217 state_.set_rollback_enabled(enabled);
218 }
219
SetIsRollback(const bool is_rollback)220 void ApexSession::SetIsRollback(const bool is_rollback) {
221 state_.set_is_rollback(is_rollback);
222 }
223
SetRollbackId(const int rollback_id)224 void ApexSession::SetRollbackId(const int rollback_id) {
225 state_.set_rollback_id(rollback_id);
226 }
227
SetCrashingNativeProcess(const std::string & crashing_process)228 void ApexSession::SetCrashingNativeProcess(
229 const std::string& crashing_process) {
230 state_.set_crashing_native_process(crashing_process);
231 }
232
AddApexName(const std::string & apex_name)233 void ApexSession::AddApexName(const std::string& apex_name) {
234 state_.add_apex_names(apex_name);
235 }
236
UpdateStateAndCommit(const SessionState::State & session_state)237 Result<void> ApexSession::UpdateStateAndCommit(
238 const SessionState::State& session_state) {
239 state_.set_state(session_state);
240
241 auto stateFilePath = getSessionStateFilePath(state_.id());
242
243 std::fstream stateFile(stateFilePath,
244 std::ios::out | std::ios::trunc | std::ios::binary);
245 if (!state_.SerializeToOstream(&stateFile)) {
246 return Error() << "Failed to write state file " << stateFilePath;
247 }
248
249 return {};
250 }
251
DeleteSession() const252 Result<void> ApexSession::DeleteSession() const {
253 return deleteSessionDir(GetId());
254 }
255
operator <<(std::ostream & out,const ApexSession & session)256 std::ostream& operator<<(std::ostream& out, const ApexSession& session) {
257 return out << "[id = " << session.GetId()
258 << "; state = " << SessionState::State_Name(session.GetState())
259 << "]";
260 }
261
DeleteFinalizedSessions()262 void ApexSession::DeleteFinalizedSessions() {
263 auto sessions = GetSessions();
264 for (const ApexSession& session : sessions) {
265 if (!session.IsFinalized()) {
266 continue;
267 }
268 auto result = session.DeleteSession();
269 if (!result.ok()) {
270 LOG(WARNING) << "Failed to delete finalized session: " << session.GetId();
271 }
272 }
273 }
274
275 } // namespace apex
276 } // namespace android
277