• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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