1 //
2 // Copyright (C) 2022 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 #include <android-base/logging.h>
17 #include <android-base/strings.h>
18 #include <curl/curl.h>
19 #include <gflags/gflags.h>
20 #include <json/json.h>
21 #include <net/if.h>
22 #include <netinet/in.h>
23 #include <string.h>
24 #include <sys/ioctl.h>
25 #include <sys/time.h>
26 #include <sys/utsname.h>
27 #include <chrono>
28 #include <ctime>
29 #include <iostream>
30
31 #include "common/libs/utils/tee_logging.h"
32 #include "host/commands/metrics/metrics_defs.h"
33 #include "host/commands/metrics/proto/cf_metrics_proto.h"
34 #include "host/commands/metrics/utils.h"
35 #include "host/libs/config/cuttlefish_config.h"
36 #include "host/libs/vm_manager/crosvm_manager.h"
37 #include "host/libs/vm_manager/qemu_manager.h"
38
39 using cuttlefish::MetricsExitCodes;
40
41 namespace metrics {
42
hashing(std::string input)43 static std::string hashing(std::string input) {
44 const std::hash<std::string> hasher;
45 return std::to_string(hasher(input));
46 }
47
osType()48 cuttlefish::MetricsEvent::OsType osType() {
49 struct utsname buf;
50 if (uname(&buf) != 0) {
51 LOG(ERROR) << "failed to retrieve system information";
52 return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_UNSPECIFIED;
53 }
54 std::string sysname(buf.sysname);
55 std::string machine(buf.machine);
56
57 if (sysname != "Linux") {
58 return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_UNSPECIFIED;
59 }
60 if (machine == "x86_64") {
61 return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_LINUX_X86_64;
62 }
63 if (machine == "x86") {
64 return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_LINUX_X86;
65 }
66 if (machine == "aarch64" || machine == "arm64") {
67 return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_LINUX_AARCH64;
68 }
69 if (machine[0] == 'a') {
70 return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_LINUX_AARCH32;
71 }
72 return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_UNSPECIFIED;
73 }
74
sessionId(uint64_t now_ms)75 std::string sessionId(uint64_t now_ms) {
76 uint64_t now_day = now_ms / 1000 / 60 / 60 / 24;
77 return hashing(macAddress() + std::to_string(now_day));
78 }
79
cfVersion()80 std::string cfVersion() {
81 // TODO: per ellisr@ leave empty for now
82 return "";
83 }
84
osVersion()85 std::string osVersion() {
86 struct utsname buf;
87 if (uname(&buf) != 0) {
88 LOG(ERROR) << "failed to retrieve system information";
89 }
90 std::string version = buf.release;
91 return version;
92 }
93
macAddress()94 std::string macAddress() {
95 int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
96 if (sock == -1) {
97 LOG(ERROR) << "couldn't connect to socket";
98 return "";
99 }
100
101 char buf2[1024];
102 struct ifconf ifc;
103 ifc.ifc_len = sizeof(buf2);
104 ifc.ifc_buf = buf2;
105 if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) {
106 LOG(ERROR) << "couldn't connect to socket";
107 return "";
108 }
109
110 struct ifreq* it = ifc.ifc_req;
111 const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq));
112
113 unsigned char mac_address[6] = {0};
114 struct ifreq ifr;
115 for (; it != end; ++it) {
116 strcpy(ifr.ifr_name, it->ifr_name);
117 if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
118 LOG(ERROR) << "couldn't connect to socket";
119 return "";
120 }
121 if (ifr.ifr_flags & IFF_LOOPBACK) {
122 continue;
123 }
124 if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) {
125 memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
126 break;
127 }
128 }
129
130 char mac[100];
131 sprintf(mac, "%02x:%02x:%02x:%02x:%02x:%02x", mac_address[0], mac_address[1],
132 mac_address[2], mac_address[3], mac_address[4], mac_address[5]);
133 return mac;
134 }
135
company()136 std::string company() {
137 // TODO: per ellisr@ leave hard-coded for now
138 return "GOOGLE";
139 }
140
vmmManager()141 cuttlefish::MetricsEvent::VmmType vmmManager() {
142 auto config = cuttlefish::CuttlefishConfig::Get();
143 CHECK(config) << "Could not open cuttlefish config";
144 auto vmm = config->vm_manager();
145 if (vmm == cuttlefish::vm_manager::CrosvmManager::name()) {
146 return cuttlefish::MetricsEvent::CUTTLEFISH_VMM_TYPE_CROSVM;
147 }
148 if (vmm == cuttlefish::vm_manager::QemuManager::name()) {
149 return cuttlefish::MetricsEvent::CUTTLEFISH_VMM_TYPE_QEMU;
150 }
151 return cuttlefish::MetricsEvent::CUTTLEFISH_VMM_TYPE_UNSPECIFIED;
152 }
153
vmmVersion()154 std::string vmmVersion() {
155 // TODO: per ellisr@ leave empty for now
156 return "";
157 }
158
epochTimeMs()159 uint64_t epochTimeMs() {
160 auto now = std::chrono::system_clock::now().time_since_epoch();
161 uint64_t milliseconds_since_epoch =
162 std::chrono::duration_cast<std::chrono::milliseconds>(now).count();
163 return milliseconds_since_epoch;
164 }
165
sampleEvent()166 cuttlefish::CuttlefishLogEvent* sampleEvent() {
167 cuttlefish::CuttlefishLogEvent* event = new cuttlefish::CuttlefishLogEvent();
168 event->set_device_type(
169 cuttlefish::CuttlefishLogEvent::CUTTLEFISH_DEVICE_TYPE_HOST);
170 return event;
171 }
172
protoToStr(LogEvent * event)173 std::string protoToStr(LogEvent* event) {
174 std::string output;
175 if (!event->SerializeToString(&output)) {
176 LOG(ERROR) << "failed to serialize proto LogEvent";
177 }
178 return output;
179 }
180
curl_out_writer(char * response,size_t size,size_t nmemb,void * userdata)181 size_t curl_out_writer([[maybe_unused]] char* response, size_t size,
182 size_t nmemb, [[maybe_unused]] void* userdata) {
183 return size * nmemb;
184 }
185
postReq(std::string output,metrics::ClearcutServer server)186 MetricsExitCodes postReq(std::string output, metrics::ClearcutServer server) {
187 const char *clearcut_scheme, *clearcut_host, *clearcut_path, *clearcut_port;
188 switch (server) {
189 case metrics::kLocal:
190 clearcut_scheme = "http";
191 clearcut_host = "localhost";
192 clearcut_path = "/log";
193 clearcut_port = "27910";
194 break;
195 case metrics::kStaging:
196 clearcut_scheme = "https";
197 clearcut_host = "play.googleapis.com";
198 clearcut_path = "/staging/log";
199 clearcut_port = "443";
200 break;
201 case metrics::kProd:
202 clearcut_scheme = "https";
203 clearcut_host = "play.googleapis.com";
204 clearcut_path = "/log";
205 clearcut_port = "443";
206 break;
207 default:
208 return cuttlefish::kInvalidHostConfiguration;
209 }
210
211 CURLU* url = curl_url();
212 CURLUcode urc = curl_url_set(url, CURLUPART_SCHEME, clearcut_scheme, 0);
213 if (urc != 0) {
214 LOG(ERROR) << "failed to set url CURLUPART_SCHEME";
215 return cuttlefish::kMetricsError;
216 }
217 urc = curl_url_set(url, CURLUPART_HOST, clearcut_host, 0);
218 if (urc != 0) {
219 LOG(ERROR) << "failed to set url CURLUPART_HOST";
220 return cuttlefish::kMetricsError;
221 }
222 urc = curl_url_set(url, CURLUPART_PATH, clearcut_path, 0);
223 if (urc != 0) {
224 LOG(ERROR) << "failed to set url CURLUPART_PATH";
225 return cuttlefish::kMetricsError;
226 }
227 urc = curl_url_set(url, CURLUPART_PORT, clearcut_port, 0);
228 if (urc != 0) {
229 LOG(ERROR) << "failed to set url CURLUPART_PORT";
230 return cuttlefish::kMetricsError;
231 }
232 curl_global_init(CURL_GLOBAL_ALL);
233 CURL* curl = curl_easy_init();
234 if (curl) {
235 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_out_writer);
236 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
237 curl_easy_setopt(curl, CURLOPT_CURLU, url);
238 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, output.c_str());
239 CURLcode rc = curl_easy_perform(curl);
240 long http_code = 0;
241 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
242 if (http_code == 200 && rc != CURLE_ABORTED_BY_CALLBACK) {
243 LOG(INFO) << "Metrics posted to ClearCut";
244 } else {
245 LOG(ERROR) << "Metrics message failed: [" << output << "]";
246 LOG(ERROR) << "http error code: " << http_code;
247 if (rc != CURLE_OK) {
248 LOG(ERROR) << "curl error code: " << rc << " | "
249 << curl_easy_strerror(rc);
250 }
251 return cuttlefish::kMetricsError;
252 }
253 curl_easy_cleanup(curl);
254 }
255 curl_url_cleanup(url);
256 curl_global_cleanup();
257 return cuttlefish::kSuccess;
258 }
259 } // namespace metrics
260