1 /*
2  * Copyright (C) 2020 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 <arpa/inet.h>
18 #include <cutils/sockets.h>
19 #include <errno.h>
20 #include <netdb.h>
21 #include <netinet/in.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <sys/socket.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 
29 #include <chrono>
30 #include <sstream>
31 
32 #include <android-base/file.h>
33 #include <android-base/logging.h>
34 #include <android-base/parseint.h>
35 #include <android-base/properties.h>
36 #include <android-base/strings.h>
37 #include <fs_mgr/file_wait.h>
38 #include <snapuserd/snapuserd_client.h>
39 
40 namespace android {
41 namespace snapshot {
42 
43 using namespace std::chrono_literals;
44 using android::base::unique_fd;
45 
EnsureSnapuserdStarted()46 bool EnsureSnapuserdStarted() {
47     if (android::base::GetProperty("init.svc.snapuserd", "") != "running") {
48         android::base::SetProperty("ctl.start", "snapuserd");
49         if (!android::base::WaitForProperty("init.svc.snapuserd", "running", 10s)) {
50             LOG(ERROR) << "Timed out waiting for snapuserd to start.";
51             return false;
52         }
53     }
54     if (!android::base::WaitForProperty("snapuserd.ready", "true", 10s)) {
55         LOG(ERROR) << "Timed out waiting for snapuserd to be ready.";
56         return false;
57     }
58     return true;
59 }
60 
SnapuserdClient(android::base::unique_fd && sockfd)61 SnapuserdClient::SnapuserdClient(android::base::unique_fd&& sockfd) : sockfd_(std::move(sockfd)) {}
62 
IsRetryErrno()63 static inline bool IsRetryErrno() {
64     return errno == ECONNREFUSED || errno == EINTR || errno == ENOENT;
65 }
66 
Connect(const std::string & socket_name,std::chrono::milliseconds timeout_ms)67 std::unique_ptr<SnapuserdClient> SnapuserdClient::Connect(const std::string& socket_name,
68                                                           std::chrono::milliseconds timeout_ms) {
69     unique_fd fd;
70     auto start = std::chrono::steady_clock::now();
71     while (true) {
72         fd.reset(socket_local_client(socket_name.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED,
73                                      SOCK_STREAM));
74         if (fd >= 0) break;
75         if (fd < 0 && !IsRetryErrno()) {
76             PLOG(ERROR) << "connect failed: " << socket_name;
77             return nullptr;
78         }
79 
80         auto now = std::chrono::steady_clock::now();
81         auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
82         if (elapsed >= timeout_ms) {
83             LOG(ERROR) << "Timed out connecting to snapuserd socket: " << socket_name;
84             return nullptr;
85         }
86 
87         std::this_thread::sleep_for(100ms);
88     }
89 
90     auto client = std::make_unique<SnapuserdClient>(std::move(fd));
91     if (!client->ValidateConnection()) {
92         return nullptr;
93     }
94     return client;
95 }
96 
WaitForServiceToTerminate(std::chrono::milliseconds timeout_ms)97 void SnapuserdClient::WaitForServiceToTerminate(std::chrono::milliseconds timeout_ms) {
98     auto start = std::chrono::steady_clock::now();
99     while (android::base::GetProperty("init.svc.snapuserd", "") == "running") {
100         auto now = std::chrono::steady_clock::now();
101         auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
102         if (elapsed >= timeout_ms) {
103             LOG(ERROR) << "Timed out - Snapuserd service did not stop - Forcefully terminating the "
104                           "service";
105             android::base::SetProperty("ctl.stop", "snapuserd");
106             return;
107         }
108         std::this_thread::sleep_for(100ms);
109     }
110 }
111 
ValidateConnection()112 bool SnapuserdClient::ValidateConnection() {
113     if (!Sendmsg("query")) {
114         return false;
115     }
116 
117     std::string str = Receivemsg();
118 
119     // If the daemon is passive then fallback to secondary active daemon. Daemon
120     // is passive during transition phase.
121     if (str.find("passive") != std::string::npos) {
122         LOG(ERROR) << "Snapuserd is terminating";
123         return false;
124     }
125 
126     if (str != "active") {
127         LOG(ERROR) << "Received failure querying daemon";
128         return false;
129     }
130     return true;
131 }
132 
Sendmsg(const std::string & msg)133 bool SnapuserdClient::Sendmsg(const std::string& msg) {
134     LOG(DEBUG) << "Sendmsg: msg " << msg << " sockfd: " << sockfd_;
135     ssize_t numBytesSent = TEMP_FAILURE_RETRY(send(sockfd_, msg.data(), msg.size(), MSG_NOSIGNAL));
136     if (numBytesSent < 0) {
137         PLOG(ERROR) << "Send failed";
138         return false;
139     }
140 
141     if ((size_t)numBytesSent < msg.size()) {
142         LOG(ERROR) << "Partial data sent, expected " << msg.size() << " bytes, sent "
143                    << numBytesSent;
144         return false;
145     }
146     return true;
147 }
148 
WaitForDeviceDelete(const std::string & control_device)149 bool SnapuserdClient::WaitForDeviceDelete(const std::string& control_device) {
150     std::string msg = "delete," + control_device;
151     if (!Sendmsg(msg)) {
152         LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
153         return false;
154     }
155     std::string response = Receivemsg();
156     if (response != "success") {
157         LOG(ERROR) << "Failed waiting to delete device " << control_device;
158         return false;
159     }
160     return true;
161 }
162 
SupportsSecondStageSocketHandoff()163 bool SnapuserdClient::SupportsSecondStageSocketHandoff() {
164     std::string msg = "supports,second_stage_socket_handoff";
165     if (!Sendmsg(msg)) {
166         LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
167         return false;
168     }
169     std::string response = Receivemsg();
170     return response == "success";
171 }
172 
Receivemsg()173 std::string SnapuserdClient::Receivemsg() {
174     char msg[PACKET_SIZE];
175     ssize_t ret = TEMP_FAILURE_RETRY(recv(sockfd_, msg, sizeof(msg), 0));
176     if (ret < 0) {
177         PLOG(ERROR) << "Snapuserd:client: recv failed";
178         return {};
179     }
180     if (ret == 0) {
181         LOG(DEBUG) << "Snapuserd:client disconnected";
182         return {};
183     }
184     return std::string(msg, ret);
185 }
186 
StopSnapuserd()187 bool SnapuserdClient::StopSnapuserd() {
188     if (!Sendmsg("stop")) {
189         LOG(ERROR) << "Failed to send stop message to snapuserd daemon";
190         return false;
191     }
192 
193     sockfd_ = {};
194     return true;
195 }
196 
AttachDmUser(const std::string & misc_name)197 bool SnapuserdClient::AttachDmUser(const std::string& misc_name) {
198     std::string msg = "start," + misc_name;
199     if (!Sendmsg(msg)) {
200         LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
201         return false;
202     }
203 
204     std::string str = Receivemsg();
205     if (str != "success") {
206         LOG(ERROR) << "Failed to receive ack for " << msg << " from snapuserd daemon";
207         return false;
208     }
209 
210     LOG(DEBUG) << "Snapuserd daemon initialized with " << msg;
211     return true;
212 }
213 
InitDmUserCow(const std::string & misc_name,const std::string & cow_device,const std::string & backing_device,const std::string & base_path_merge)214 uint64_t SnapuserdClient::InitDmUserCow(const std::string& misc_name, const std::string& cow_device,
215                                         const std::string& backing_device,
216                                         const std::string& base_path_merge) {
217     std::vector<std::string> parts;
218 
219     if (base_path_merge.empty()) {
220         parts = {"init", misc_name, cow_device, backing_device};
221     } else {
222         // For userspace snapshots
223         parts = {"init", misc_name, cow_device, backing_device, base_path_merge};
224     }
225     std::string msg = android::base::Join(parts, ",");
226     if (!Sendmsg(msg)) {
227         LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
228         return 0;
229     }
230 
231     std::string str = Receivemsg();
232 
233     std::vector<std::string> input = android::base::Split(str, ",");
234 
235     if (input.empty() || input[0] != "success") {
236         LOG(ERROR) << "Failed to receive number of sectors for " << msg << " from snapuserd daemon";
237         return 0;
238     }
239 
240     LOG(DEBUG) << "Snapuserd daemon COW device initialized: " << cow_device
241                << " Num-sectors: " << input[1];
242 
243     uint64_t num_sectors = 0;
244     if (!android::base::ParseUint(input[1], &num_sectors)) {
245         LOG(ERROR) << "Failed to parse input string to sectors";
246         return 0;
247     }
248     return num_sectors;
249 }
250 
DetachSnapuserd()251 bool SnapuserdClient::DetachSnapuserd() {
252     if (!Sendmsg("detach")) {
253         LOG(ERROR) << "Failed to detach snapuserd.";
254         return false;
255     }
256 
257     WaitForServiceToTerminate(3s);
258     return true;
259 }
260 
InitiateMerge(const std::string & misc_name)261 bool SnapuserdClient::InitiateMerge(const std::string& misc_name) {
262     std::string msg = "initiate_merge," + misc_name;
263     if (!Sendmsg(msg)) {
264         LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
265         return false;
266     }
267     std::string response = Receivemsg();
268     return response == "success";
269 }
270 
GetMergePercent()271 double SnapuserdClient::GetMergePercent() {
272     std::string msg = "merge_percent";
273     if (!Sendmsg(msg)) {
274         LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
275         return false;
276     }
277     std::string response = Receivemsg();
278 
279     return std::stod(response);
280 }
281 
QuerySnapshotStatus(const std::string & misc_name)282 std::string SnapuserdClient::QuerySnapshotStatus(const std::string& misc_name) {
283     std::string msg = "getstatus," + misc_name;
284     if (!Sendmsg(msg)) {
285         LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
286         return "snapshot-merge-failed";
287     }
288     return Receivemsg();
289 }
290 
QueryUpdateVerification()291 bool SnapuserdClient::QueryUpdateVerification() {
292     std::string msg = "update-verify";
293     if (!Sendmsg(msg)) {
294         LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
295         return false;
296     }
297     std::string response = Receivemsg();
298     return response == "success";
299 }
300 
GetDaemonAliveIndicatorPath()301 std::string SnapuserdClient::GetDaemonAliveIndicatorPath() {
302     return "/metadata/ota/" + std::string(kDaemonAliveIndicator);
303 }
304 
IsTransitionedDaemonReady()305 bool SnapuserdClient::IsTransitionedDaemonReady() {
306     if (!android::fs_mgr::WaitForFile(GetDaemonAliveIndicatorPath(), 10s)) {
307         LOG(ERROR) << "Timed out waiting for daemon indicator path: "
308                    << GetDaemonAliveIndicatorPath();
309         return false;
310     }
311 
312     return true;
313 }
314 
RemoveTransitionedDaemonIndicator()315 bool SnapuserdClient::RemoveTransitionedDaemonIndicator() {
316     std::string error;
317     std::string filePath = GetDaemonAliveIndicatorPath();
318     if (!android::base::RemoveFileIfExists(filePath, &error)) {
319         LOG(ERROR) << "Failed to remove DaemonAliveIndicatorPath - error: " << error;
320         return false;
321     }
322 
323     if (!android::fs_mgr::WaitForFileDeleted(filePath, 5s)) {
324         LOG(ERROR) << "Timed out waiting for " << filePath << " to unlink";
325         return false;
326     }
327 
328     return true;
329 }
330 
NotifyTransitionDaemonIsReady()331 void SnapuserdClient::NotifyTransitionDaemonIsReady() {
332     if (!android::base::WriteStringToFile("1", GetDaemonAliveIndicatorPath())) {
333         PLOG(ERROR) << "Unable to write daemon alive indicator path: "
334                     << GetDaemonAliveIndicatorPath();
335     }
336 }
337 
338 }  // namespace snapshot
339 }  // namespace android
340