blob: 98180b77c9e42bc48d687b04c7d3b75f079f03b0 [file] [log] [blame]
Martijn Coenencabc92f2019-01-11 10:50:35 +01001/*
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
Martijn Coenenc11b68b2019-01-15 11:28:11 +010017#include "apexd_session.h"
Andreas Gampef35e1792019-04-01 15:58:03 -070018
Martijn Coenen610909b2019-01-18 13:49:38 +010019#include "apexd_utils.h"
Martijn Coenencabc92f2019-01-11 10:50:35 +010020#include "status_or.h"
21#include "string_log.h"
22
23#include "session_state.pb.h"
24
25#include <android-base/logging.h>
Martijn Coenen610909b2019-01-18 13:49:38 +010026#include <dirent.h>
Martijn Coenencabc92f2019-01-11 10:50:35 +010027#include <sys/stat.h>
28
Nikita Ioffe53c3dcd2019-02-08 17:39:00 +000029#include <filesystem>
Martijn Coenencabc92f2019-01-11 10:50:35 +010030#include <fstream>
Nikita Ioffe463d4e82019-02-10 18:46:20 +000031#include <optional>
Nikita Ioffe53c3dcd2019-02-08 17:39:00 +000032
Martijn Coenencabc92f2019-01-11 10:50:35 +010033using apex::proto::SessionState;
34
35namespace android {
36namespace apex {
37
38namespace {
39
Martijn Coenend32d1cb2019-02-13 12:43:57 +010040static constexpr const char* kStateFileName = "state";
41
Martijn Coenen610909b2019-01-18 13:49:38 +010042std::string getSessionDir(int session_id) {
43 return kApexSessionsDir + "/" + std::to_string(session_id);
Martijn Coenencabc92f2019-01-11 10:50:35 +010044}
45
Martijn Coenen610909b2019-01-18 13:49:38 +010046std::string getSessionStateFilePath(int session_id) {
Martijn Coenend32d1cb2019-02-13 12:43:57 +010047 return getSessionDir(session_id) + "/" + kStateFileName;
Martijn Coenencabc92f2019-01-11 10:50:35 +010048}
49
Martijn Coenen610909b2019-01-18 13:49:38 +010050StatusOr<std::string> createSessionDirIfNeeded(int session_id) {
Martijn Coenencabc92f2019-01-11 10:50:35 +010051 // create /data/sessions
Nikita Ioffea8453da2019-01-30 21:29:13 +000052 auto res = createDirIfNeeded(kApexSessionsDir, 0700);
Martijn Coenencabc92f2019-01-11 10:50:35 +010053 if (!res.Ok()) {
54 return StatusOr<std::string>(res.ErrorMessage());
55 }
Martijn Coenen610909b2019-01-18 13:49:38 +010056 // create /data/sessions/session_id
Martijn Coenencabc92f2019-01-11 10:50:35 +010057 std::string sessionDir = getSessionDir(session_id);
Nikita Ioffea8453da2019-01-30 21:29:13 +000058 res = createDirIfNeeded(sessionDir, 0700);
Martijn Coenencabc92f2019-01-11 10:50:35 +010059 if (!res.Ok()) {
60 return StatusOr<std::string>(res.ErrorMessage());
61 }
62
63 return StatusOr<std::string>(sessionDir);
64}
65
Nikita Ioffe53c3dcd2019-02-08 17:39:00 +000066Status deleteSessionDir(int session_id) {
67 std::string session_dir = getSessionDir(session_id);
68 LOG(DEBUG) << "Deleting " << session_dir;
69 auto path = std::filesystem::path(session_dir);
70 std::error_code error_code;
71 std::filesystem::remove_all(path, error_code);
72 if (error_code) {
73 return Status::Fail(StringLog() << "Failed to delete " << session_dir
74 << " : " << error_code);
75 }
76 return Status::Success();
77}
78
Martijn Coenen610909b2019-01-18 13:49:38 +010079} // namespace
80
Martijn Coenend32d1cb2019-02-13 12:43:57 +010081ApexSession::ApexSession(const SessionState& state) : state_(state) {}
Martijn Coenen610909b2019-01-18 13:49:38 +010082
83StatusOr<ApexSession> ApexSession::CreateSession(int session_id) {
Martijn Coenend32d1cb2019-02-13 12:43:57 +010084 SessionState state;
Martijn Coenen610909b2019-01-18 13:49:38 +010085 // Create session directory
86 auto sessionPath = createSessionDirIfNeeded(session_id);
87 if (!sessionPath.Ok()) {
88 return StatusOr<ApexSession>::MakeError(sessionPath.ErrorMessage());
89 }
Martijn Coenend32d1cb2019-02-13 12:43:57 +010090 state.set_id(session_id);
91 ApexSession session(state);
Martijn Coenen610909b2019-01-18 13:49:38 +010092
93 return StatusOr<ApexSession>(std::move(session));
94}
Martijn Coenend32d1cb2019-02-13 12:43:57 +010095StatusOr<ApexSession> ApexSession::GetSessionFromFile(const std::string& path) {
Martijn Coenen610909b2019-01-18 13:49:38 +010096 SessionState state;
Martijn Coenen610909b2019-01-18 13:49:38 +010097 std::fstream stateFile(path, std::ios::in | std::ios::binary);
98 if (!stateFile) {
99 return StatusOr<ApexSession>::MakeError("Failed to open " + path);
Martijn Coenencabc92f2019-01-11 10:50:35 +0100100 }
101
Martijn Coenen610909b2019-01-18 13:49:38 +0100102 if (!state.ParseFromIstream(&stateFile)) {
103 return StatusOr<ApexSession>::MakeError("Failed to parse " + path);
104 }
105
Martijn Coenend32d1cb2019-02-13 12:43:57 +0100106 return StatusOr<ApexSession>(ApexSession(state));
107}
108
109StatusOr<ApexSession> ApexSession::GetSession(int session_id) {
110 auto path = getSessionStateFilePath(session_id);
111
112 return GetSessionFromFile(path);
Martijn Coenen610909b2019-01-18 13:49:38 +0100113}
114
115std::vector<ApexSession> ApexSession::GetSessions() {
116 std::vector<ApexSession> sessions;
117
Mohammad Samiul Islamd428a182019-03-19 14:36:24 +0000118 StatusOr<std::vector<std::string>> sessionPaths = ReadDir(
119 kApexSessionsDir, [](const std::filesystem::directory_entry& entry) {
120 std::error_code ec;
121 return entry.is_directory(ec);
Martijn Coenen610909b2019-01-18 13:49:38 +0100122 });
123
124 if (!sessionPaths.Ok()) {
125 return sessions;
126 }
127
Nikita Ioffe6bea4e52019-02-10 22:46:05 +0000128 for (const std::string& sessionDirPath : *sessionPaths) {
Martijn Coenen610909b2019-01-18 13:49:38 +0100129 // Try to read session state
Martijn Coenend32d1cb2019-02-13 12:43:57 +0100130 auto session = GetSessionFromFile(sessionDirPath + "/" + kStateFileName);
Martijn Coenen610909b2019-01-18 13:49:38 +0100131 if (!session.Ok()) {
132 LOG(WARNING) << session.ErrorMessage();
133 continue;
134 }
135 sessions.push_back(std::move(*session));
136 }
137
138 return sessions;
139}
140
141std::vector<ApexSession> ApexSession::GetSessionsInState(
142 SessionState::State state) {
143 auto sessions = GetSessions();
144 sessions.erase(
145 std::remove_if(sessions.begin(), sessions.end(),
Yi Kongc7a78122019-03-08 14:31:24 -0800146 [&](const ApexSession &s) { return s.GetState() != state; }),
Martijn Coenen610909b2019-01-18 13:49:38 +0100147 sessions.end());
148
149 return sessions;
150}
151
Nikita Ioffe463d4e82019-02-10 18:46:20 +0000152StatusOr<std::optional<ApexSession>> ApexSession::GetActiveSession() {
153 auto sessions = GetSessions();
154 std::optional<ApexSession> ret = std::nullopt;
155 for (const ApexSession& session : sessions) {
156 if (!session.IsFinalized()) {
157 if (ret) {
158 return StatusOr<std::optional<ApexSession>>::MakeError(
159 "More than one active session");
160 }
161 ret.emplace(session);
162 }
163 }
164 return StatusOr<std::optional<ApexSession>>(std::move(ret));
165}
166
Martijn Coenen610909b2019-01-18 13:49:38 +0100167SessionState::State ApexSession::GetState() const { return state_.state(); }
168
Martijn Coenend32d1cb2019-02-13 12:43:57 +0100169int ApexSession::GetId() const { return state_.id(); }
Martijn Coenen610909b2019-01-18 13:49:38 +0100170
Gavin Corkerya41373a2019-09-26 12:53:45 +0100171std::string ApexSession::GetBuildFingerprint() const {
172 return state_.expected_build_fingerprint();
173}
174
Nikita Ioffe463d4e82019-02-10 18:46:20 +0000175bool ApexSession::IsFinalized() const {
176 switch (GetState()) {
177 case SessionState::SUCCESS:
178 [[fallthrough]];
179 case SessionState::ACTIVATION_FAILED:
Nikita Ioffe9ae986a2019-02-18 22:39:27 +0000180 [[fallthrough]];
181 case SessionState::ROLLED_BACK:
Nikita Ioffe6f87d542019-04-09 13:09:26 +0100182 [[fallthrough]];
183 case SessionState::ROLLBACK_FAILED:
Nikita Ioffe463d4e82019-02-10 18:46:20 +0000184 return true;
185 default:
186 return false;
187 }
188}
189
Dario Freni6dd4dd62019-01-18 12:45:44 +0000190const google::protobuf::RepeatedField<int> ApexSession::GetChildSessionIds()
191 const {
192 return state_.child_session_ids();
193}
194
195void ApexSession::SetChildSessionIds(
196 const std::vector<int>& child_session_ids) {
197 *(state_.mutable_child_session_ids()) = {child_session_ids.begin(),
198 child_session_ids.end()};
199}
200
Gavin Corkerya41373a2019-09-26 12:53:45 +0100201void ApexSession::SetBuildFingerprint(const std::string& fingerprint) {
202 *(state_.mutable_expected_build_fingerprint()) = fingerprint;
203}
204
Nikita Ioffea0c0ccb2019-02-12 22:00:41 +0000205Status ApexSession::UpdateStateAndCommit(
206 const SessionState::State& session_state) {
Martijn Coenen610909b2019-01-18 13:49:38 +0100207 state_.set_state(session_state);
208
Martijn Coenend32d1cb2019-02-13 12:43:57 +0100209 auto stateFilePath = getSessionStateFilePath(state_.id());
Martijn Coenen610909b2019-01-18 13:49:38 +0100210
Martijn Coenencabc92f2019-01-11 10:50:35 +0100211 std::fstream stateFile(stateFilePath,
212 std::ios::out | std::ios::trunc | std::ios::binary);
Martijn Coenen610909b2019-01-18 13:49:38 +0100213 if (!state_.SerializeToOstream(&stateFile)) {
Martijn Coenencabc92f2019-01-11 10:50:35 +0100214 return Status::Fail("Failed to write state file " + stateFilePath);
215 }
216
217 return Status::Success();
218}
219
Nikita Ioffe9bd28b92019-03-04 22:18:07 +0000220Status ApexSession::DeleteSession() const { return deleteSessionDir(GetId()); }
Nikita Ioffe53c3dcd2019-02-08 17:39:00 +0000221
Nikita Ioffea0c0ccb2019-02-12 22:00:41 +0000222std::ostream& operator<<(std::ostream& out, const ApexSession& session) {
223 return out << "[id = " << session.GetId()
224 << "; state = " << SessionState::State_Name(session.GetState())
225 << "]";
226}
227
Martijn Coenencabc92f2019-01-11 10:50:35 +0100228} // namespace apex
229} // namespace android