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