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