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/logging.h>
33 #include <android-base/parseint.h>
34 #include <android-base/properties.h>
35 #include <android-base/strings.h>
36 #include <libsnapshot/snapuserd_client.h>
37
38 namespace android {
39 namespace snapshot {
40
41 using namespace std::chrono_literals;
42 using android::base::unique_fd;
43
EnsureSnapuserdStarted()44 bool EnsureSnapuserdStarted() {
45 if (android::base::GetProperty("init.svc.snapuserd", "") == "running") {
46 return true;
47 }
48
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 return true;
55 }
56
SnapuserdClient(android::base::unique_fd && sockfd)57 SnapuserdClient::SnapuserdClient(android::base::unique_fd&& sockfd) : sockfd_(std::move(sockfd)) {}
58
IsRetryErrno()59 static inline bool IsRetryErrno() {
60 return errno == ECONNREFUSED || errno == EINTR || errno == ENOENT;
61 }
62
Connect(const std::string & socket_name,std::chrono::milliseconds timeout_ms)63 std::unique_ptr<SnapuserdClient> SnapuserdClient::Connect(const std::string& socket_name,
64 std::chrono::milliseconds timeout_ms) {
65 unique_fd fd;
66 auto start = std::chrono::steady_clock::now();
67 while (true) {
68 fd.reset(socket_local_client(socket_name.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED,
69 SOCK_STREAM));
70 if (fd >= 0) break;
71 if (fd < 0 && !IsRetryErrno()) {
72 PLOG(ERROR) << "connect failed: " << socket_name;
73 return nullptr;
74 }
75
76 auto now = std::chrono::steady_clock::now();
77 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
78 if (elapsed >= timeout_ms) {
79 LOG(ERROR) << "Timed out connecting to snapuserd socket: " << socket_name;
80 return nullptr;
81 }
82
83 std::this_thread::sleep_for(100ms);
84 }
85
86 auto client = std::make_unique<SnapuserdClient>(std::move(fd));
87 if (!client->ValidateConnection()) {
88 return nullptr;
89 }
90 return client;
91 }
92
ValidateConnection()93 bool SnapuserdClient::ValidateConnection() {
94 if (!Sendmsg("query")) {
95 return false;
96 }
97
98 std::string str = Receivemsg();
99
100 // If the daemon is passive then fallback to secondary active daemon. Daemon
101 // is passive during transition phase.
102 if (str.find("passive") != std::string::npos) {
103 LOG(ERROR) << "Snapuserd is terminating";
104 return false;
105 }
106
107 if (str != "active") {
108 LOG(ERROR) << "Received failure querying daemon";
109 return false;
110 }
111 return true;
112 }
113
Sendmsg(const std::string & msg)114 bool SnapuserdClient::Sendmsg(const std::string& msg) {
115 LOG(DEBUG) << "Sendmsg: msg " << msg << " sockfd: " << sockfd_;
116 ssize_t numBytesSent = TEMP_FAILURE_RETRY(send(sockfd_, msg.data(), msg.size(), MSG_NOSIGNAL));
117 if (numBytesSent < 0) {
118 PLOG(ERROR) << "Send failed";
119 return false;
120 }
121
122 if ((size_t)numBytesSent < msg.size()) {
123 LOG(ERROR) << "Partial data sent, expected " << msg.size() << " bytes, sent "
124 << numBytesSent;
125 return false;
126 }
127 return true;
128 }
129
WaitForDeviceDelete(const std::string & control_device)130 bool SnapuserdClient::WaitForDeviceDelete(const std::string& control_device) {
131 std::string msg = "delete," + control_device;
132 if (!Sendmsg(msg)) {
133 LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
134 return false;
135 }
136 std::string response = Receivemsg();
137 if (response != "success") {
138 LOG(ERROR) << "Failed waiting to delete device " << control_device;
139 return false;
140 }
141 return true;
142 }
143
Receivemsg()144 std::string SnapuserdClient::Receivemsg() {
145 char msg[PACKET_SIZE];
146 ssize_t ret = TEMP_FAILURE_RETRY(recv(sockfd_, msg, sizeof(msg), 0));
147 if (ret < 0) {
148 PLOG(ERROR) << "Snapuserd:client: recv failed";
149 return {};
150 }
151 if (ret == 0) {
152 LOG(DEBUG) << "Snapuserd:client disconnected";
153 return {};
154 }
155 return std::string(msg, ret);
156 }
157
StopSnapuserd()158 bool SnapuserdClient::StopSnapuserd() {
159 if (!Sendmsg("stop")) {
160 LOG(ERROR) << "Failed to send stop message to snapuserd daemon";
161 return false;
162 }
163
164 sockfd_ = {};
165 return true;
166 }
167
AttachDmUser(const std::string & misc_name)168 bool SnapuserdClient::AttachDmUser(const std::string& misc_name) {
169 std::string msg = "start," + misc_name;
170 if (!Sendmsg(msg)) {
171 LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
172 return false;
173 }
174
175 std::string str = Receivemsg();
176 if (str != "success") {
177 LOG(ERROR) << "Failed to receive ack for " << msg << " from snapuserd daemon";
178 return false;
179 }
180
181 LOG(DEBUG) << "Snapuserd daemon initialized with " << msg;
182 return true;
183 }
184
InitDmUserCow(const std::string & misc_name,const std::string & cow_device,const std::string & backing_device)185 uint64_t SnapuserdClient::InitDmUserCow(const std::string& misc_name, const std::string& cow_device,
186 const std::string& backing_device) {
187 std::vector<std::string> parts = {"init", misc_name, cow_device, backing_device};
188 std::string msg = android::base::Join(parts, ",");
189 if (!Sendmsg(msg)) {
190 LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
191 return 0;
192 }
193
194 std::string str = Receivemsg();
195
196 std::vector<std::string> input = android::base::Split(str, ",");
197
198 if (input.empty() || input[0] != "success") {
199 LOG(ERROR) << "Failed to receive number of sectors for " << msg << " from snapuserd daemon";
200 return 0;
201 }
202
203 LOG(DEBUG) << "Snapuserd daemon COW device initialized: " << cow_device
204 << " Num-sectors: " << input[1];
205
206 uint64_t num_sectors = 0;
207 if (!android::base::ParseUint(input[1], &num_sectors)) {
208 LOG(ERROR) << "Failed to parse input string to sectors";
209 return 0;
210 }
211 return num_sectors;
212 }
213
DetachSnapuserd()214 bool SnapuserdClient::DetachSnapuserd() {
215 if (!Sendmsg("detach")) {
216 LOG(ERROR) << "Failed to detach snapuserd.";
217 return false;
218 }
219 return true;
220 }
221
222 } // namespace snapshot
223 } // namespace android
224