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