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 <thread>
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 
TryConnect(const std::string & socket_name,std::chrono::milliseconds timeout_ms)67 std::unique_ptr<SnapuserdClient> SnapuserdClient::TryConnect(const std::string& socket_name,
68                                                              std::chrono::milliseconds timeout_ms) {
69     unique_fd fd;
70     const auto start = std::chrono::steady_clock::now();
71     while (true) {
72         fd.reset(TEMP_FAILURE_RETRY(socket_local_client(
73                 socket_name.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM)));
74         if (fd >= 0) {
75             auto client = std::make_unique<SnapuserdClient>(std::move(fd));
76             if (!client->ValidateConnection()) {
77                 return nullptr;
78             }
79             return client;
80         }
81         if (errno == ENOENT) {
82             LOG(INFO) << "Daemon socket " << socket_name
83                       << " does not exist, return without waiting.";
84             return nullptr;
85         }
86         if (errno == ECONNREFUSED) {
87             const auto now = std::chrono::steady_clock::now();
88             const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
89             if (elapsed >= timeout_ms) {
90                 LOG(ERROR) << "Timed out connecting to snapuserd socket: " << socket_name;
91                 return nullptr;
92             }
93             std::this_thread::sleep_for(10ms);
94         } else {
95             PLOG(ERROR) << "connect failed: " << socket_name;
96             return nullptr;
97         }
98     }
99 }
100 
Connect(const std::string & socket_name,std::chrono::milliseconds timeout_ms)101 std::unique_ptr<SnapuserdClient> SnapuserdClient::Connect(const std::string& socket_name,
102                                                           std::chrono::milliseconds timeout_ms) {
103     unique_fd fd;
104     auto start = std::chrono::steady_clock::now();
105     while (true) {
106         fd.reset(socket_local_client(socket_name.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED,
107                                      SOCK_STREAM));
108         if (fd >= 0) break;
109         if (fd < 0 && !IsRetryErrno()) {
110             PLOG(ERROR) << "connect failed: " << socket_name;
111             return nullptr;
112         }
113 
114         auto now = std::chrono::steady_clock::now();
115         auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
116         if (elapsed >= timeout_ms) {
117             LOG(ERROR) << "Timed out connecting to snapuserd socket: " << socket_name;
118             return nullptr;
119         }
120 
121         std::this_thread::sleep_for(100ms);
122     }
123 
124     auto client = std::make_unique<SnapuserdClient>(std::move(fd));
125     if (!client->ValidateConnection()) {
126         return nullptr;
127     }
128     return client;
129 }
130 
WaitForServiceToTerminate(std::chrono::milliseconds timeout_ms)131 void SnapuserdClient::WaitForServiceToTerminate(std::chrono::milliseconds timeout_ms) {
132     auto start = std::chrono::steady_clock::now();
133     while (android::base::GetProperty("init.svc.snapuserd", "") == "running") {
134         auto now = std::chrono::steady_clock::now();
135         auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
136         if (elapsed >= timeout_ms) {
137             LOG(ERROR) << "Timed out - Snapuserd service did not stop - Forcefully terminating the "
138                           "service";
139             android::base::SetProperty("ctl.stop", "snapuserd");
140             return;
141         }
142         std::this_thread::sleep_for(100ms);
143     }
144 }
145 
ValidateConnection()146 bool SnapuserdClient::ValidateConnection() {
147     if (!Sendmsg("query")) {
148         return false;
149     }
150 
151     std::string str = Receivemsg();
152 
153     // If the daemon is passive then fallback to secondary active daemon. Daemon
154     // is passive during transition phase.
155     if (str.find("passive") != std::string::npos) {
156         LOG(ERROR) << "Snapuserd is terminating";
157         return false;
158     }
159 
160     if (str != "active") {
161         LOG(ERROR) << "Received failure querying daemon";
162         return false;
163     }
164     return true;
165 }
166 
Sendmsg(const std::string & msg)167 bool SnapuserdClient::Sendmsg(const std::string& msg) {
168     LOG(DEBUG) << "Sendmsg: msg " << msg << " sockfd: " << sockfd_;
169     ssize_t numBytesSent = TEMP_FAILURE_RETRY(send(sockfd_, msg.data(), msg.size(), MSG_NOSIGNAL));
170     if (numBytesSent < 0) {
171         PLOG(ERROR) << "Send failed";
172         return false;
173     }
174 
175     if ((size_t)numBytesSent < msg.size()) {
176         LOG(ERROR) << "Partial data sent, expected " << msg.size() << " bytes, sent "
177                    << numBytesSent;
178         return false;
179     }
180     return true;
181 }
182 
WaitForDeviceDelete(const std::string & control_device)183 bool SnapuserdClient::WaitForDeviceDelete(const std::string& control_device) {
184     std::string msg = "delete," + control_device;
185     if (!Sendmsg(msg)) {
186         LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
187         return false;
188     }
189     std::string response = Receivemsg();
190     if (response != "success") {
191         LOG(ERROR) << "Failed waiting to delete device " << control_device;
192         return false;
193     }
194     return true;
195 }
196 
SupportsSecondStageSocketHandoff()197 bool SnapuserdClient::SupportsSecondStageSocketHandoff() {
198     std::string msg = "supports,second_stage_socket_handoff";
199     if (!Sendmsg(msg)) {
200         LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
201         return false;
202     }
203     std::string response = Receivemsg();
204     return response == "success";
205 }
206 
Receivemsg()207 std::string SnapuserdClient::Receivemsg() {
208     char msg[PACKET_SIZE];
209     ssize_t ret = TEMP_FAILURE_RETRY(recv(sockfd_, msg, sizeof(msg), 0));
210     if (ret < 0) {
211         PLOG(ERROR) << "Snapuserd:client: recv failed";
212         return {};
213     }
214     if (ret == 0) {
215         LOG(DEBUG) << "Snapuserd:client disconnected";
216         return {};
217     }
218     return std::string(msg, ret);
219 }
220 
StopSnapuserd()221 bool SnapuserdClient::StopSnapuserd() {
222     if (!Sendmsg("stop")) {
223         LOG(ERROR) << "Failed to send stop message to snapuserd daemon";
224         return false;
225     }
226 
227     sockfd_ = {};
228     return true;
229 }
230 
AttachDmUser(const std::string & misc_name)231 bool SnapuserdClient::AttachDmUser(const std::string& misc_name) {
232     std::string msg = "start," + misc_name;
233     if (!Sendmsg(msg)) {
234         LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
235         return false;
236     }
237 
238     std::string str = Receivemsg();
239     if (str != "success") {
240         LOG(ERROR) << "Failed to receive ack for " << msg << " from snapuserd daemon";
241         return false;
242     }
243 
244     LOG(DEBUG) << "Snapuserd daemon initialized with " << msg;
245     return true;
246 }
247 
InitDmUserCow(const std::string & misc_name,const std::string & cow_device,const std::string & backing_device,const std::string & base_path_merge)248 uint64_t SnapuserdClient::InitDmUserCow(const std::string& misc_name, const std::string& cow_device,
249                                         const std::string& backing_device,
250                                         const std::string& base_path_merge) {
251     std::vector<std::string> parts;
252 
253     if (base_path_merge.empty()) {
254         parts = {"init", misc_name, cow_device, backing_device};
255     } else {
256         // For userspace snapshots
257         parts = {"init", misc_name, cow_device, backing_device, base_path_merge};
258     }
259     std::string msg = android::base::Join(parts, ",");
260     if (!Sendmsg(msg)) {
261         LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
262         return 0;
263     }
264 
265     std::string str = Receivemsg();
266 
267     std::vector<std::string> input = android::base::Split(str, ",");
268 
269     if (input.empty() || input[0] != "success") {
270         LOG(ERROR) << "Failed to receive number of sectors for " << msg << " from snapuserd daemon";
271         return 0;
272     }
273 
274     LOG(DEBUG) << "Snapuserd daemon COW device initialized: " << cow_device
275                << " Num-sectors: " << input[1];
276 
277     uint64_t num_sectors = 0;
278     if (!android::base::ParseUint(input[1], &num_sectors)) {
279         LOG(ERROR) << "Failed to parse input string to sectors";
280         return 0;
281     }
282     return num_sectors;
283 }
284 
DetachSnapuserd()285 bool SnapuserdClient::DetachSnapuserd() {
286     if (!Sendmsg("detach")) {
287         LOG(ERROR) << "Failed to detach snapuserd.";
288         return false;
289     }
290 
291     WaitForServiceToTerminate(3s);
292     return true;
293 }
294 
InitiateMerge(const std::string & misc_name)295 bool SnapuserdClient::InitiateMerge(const std::string& misc_name) {
296     std::string msg = "initiate_merge," + misc_name;
297     if (!Sendmsg(msg)) {
298         LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
299         return false;
300     }
301     std::string response = Receivemsg();
302     return response == "success";
303 }
304 
GetMergePercent()305 double SnapuserdClient::GetMergePercent() {
306     std::string msg = "merge_percent";
307     if (!Sendmsg(msg)) {
308         LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
309         return false;
310     }
311     std::string response = Receivemsg();
312 
313     return std::stod(response);
314 }
315 
QuerySnapshotStatus(const std::string & misc_name)316 std::string SnapuserdClient::QuerySnapshotStatus(const std::string& misc_name) {
317     std::string msg = "getstatus," + misc_name;
318     if (!Sendmsg(msg)) {
319         LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
320         return "snapshot-merge-failed";
321     }
322     return Receivemsg();
323 }
324 
QueryUpdateVerification()325 bool SnapuserdClient::QueryUpdateVerification() {
326     std::string msg = "update-verify";
327     if (!Sendmsg(msg)) {
328         LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
329         return false;
330     }
331     std::string response = Receivemsg();
332     return response == "success";
333 }
334 
GetDaemonAliveIndicatorPath()335 std::string SnapuserdClient::GetDaemonAliveIndicatorPath() {
336     return "/metadata/ota/" + std::string(kDaemonAliveIndicator);
337 }
338 
IsTransitionedDaemonReady()339 bool SnapuserdClient::IsTransitionedDaemonReady() {
340     if (!android::fs_mgr::WaitForFile(GetDaemonAliveIndicatorPath(), 10s)) {
341         LOG(ERROR) << "Timed out waiting for daemon indicator path: "
342                    << GetDaemonAliveIndicatorPath();
343         return false;
344     }
345 
346     return true;
347 }
348 
RemoveTransitionedDaemonIndicator()349 bool SnapuserdClient::RemoveTransitionedDaemonIndicator() {
350     std::string error;
351     std::string filePath = GetDaemonAliveIndicatorPath();
352     if (!android::base::RemoveFileIfExists(filePath, &error)) {
353         LOG(ERROR) << "Failed to remove DaemonAliveIndicatorPath - error: " << error;
354         return false;
355     }
356 
357     if (!android::fs_mgr::WaitForFileDeleted(filePath, 5s)) {
358         LOG(ERROR) << "Timed out waiting for " << filePath << " to unlink";
359         return false;
360     }
361 
362     return true;
363 }
364 
NotifyTransitionDaemonIsReady()365 void SnapuserdClient::NotifyTransitionDaemonIsReady() {
366     if (!android::base::WriteStringToFile("1", GetDaemonAliveIndicatorPath())) {
367         PLOG(ERROR) << "Unable to write daemon alive indicator path: "
368                     << GetDaemonAliveIndicatorPath();
369     }
370 }
371 
372 }  // namespace snapshot
373 }  // namespace android
374