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 <net/if.h>
21 #include <netinet/in.h>
22 #include <string.h>
23 #include <sys/ioctl.h>
24 #include <sys/time.h>
25 #include <sys/utsname.h>
26 #include <chrono>
27 #include <ctime>
28 #include <iostream>
29
30 #include "common/libs/utils/tee_logging.h"
31 #include "host/commands/metrics/metrics_defs.h"
32 #include "host/commands/metrics/utils.h"
33
34 namespace cuttlefish::metrics {
35
Hashing(const std::string & input)36 static std::string Hashing(const std::string& input) {
37 const std::hash<std::string> hasher;
38 return std::to_string(hasher(input));
39 }
40
GetOsName()41 std::string GetOsName() {
42 struct utsname buf;
43 if (uname(&buf) != 0) {
44 LOG(ERROR) << "failed to retrieve system information";
45 return "Error";
46 }
47 return std::string(buf.sysname);
48 }
49
GenerateSessionId(uint64_t now_ms)50 std::string GenerateSessionId(uint64_t now_ms) {
51 uint64_t now_day = now_ms / 1000 / 60 / 60 / 24;
52 return Hashing(GetMacAddress() + std::to_string(now_day));
53 }
54
GetCfVersion()55 std::string GetCfVersion() {
56 // TODO: per ellisr@ leave empty for now
57 return "";
58 }
59
GetOsVersion()60 std::string GetOsVersion() {
61 struct utsname buf;
62 if (uname(&buf) != 0) {
63 LOG(ERROR) << "failed to retrieve system information";
64 }
65 std::string version = buf.release;
66 return version;
67 }
68
GetMacAddress()69 std::string GetMacAddress() {
70 int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
71 if (sock == -1) {
72 LOG(ERROR) << "couldn't connect to socket";
73 return "";
74 }
75
76 char buf2[1024];
77 struct ifconf ifc;
78 ifc.ifc_len = sizeof(buf2);
79 ifc.ifc_buf = buf2;
80 if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) {
81 LOG(ERROR) << "couldn't connect to socket";
82 return "";
83 }
84
85 struct ifreq* it = ifc.ifc_req;
86 const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq));
87
88 unsigned char mac_address[6] = {0};
89 struct ifreq ifr;
90 for (; it != end; ++it) {
91 strcpy(ifr.ifr_name, it->ifr_name);
92 if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
93 LOG(ERROR) << "couldn't connect to socket";
94 return "";
95 }
96 if (ifr.ifr_flags & IFF_LOOPBACK) {
97 continue;
98 }
99 if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) {
100 memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
101 break;
102 }
103 }
104
105 char mac[100];
106 sprintf(mac, "%02x:%02x:%02x:%02x:%02x:%02x", mac_address[0], mac_address[1],
107 mac_address[2], mac_address[3], mac_address[4], mac_address[5]);
108 return mac;
109 }
110
GetCompany()111 std::string GetCompany() {
112 // TODO: per ellisr@ leave hard-coded for now
113 return "GOOGLE";
114 }
115
GetVmmVersion()116 std::string GetVmmVersion() {
117 // TODO: per ellisr@ leave empty for now
118 return "";
119 }
120
GetEpochTimeMs()121 uint64_t GetEpochTimeMs() {
122 auto now = std::chrono::system_clock::now().time_since_epoch();
123 uint64_t milliseconds_since_epoch =
124 std::chrono::duration_cast<std::chrono::milliseconds>(now).count();
125 return milliseconds_since_epoch;
126 }
127
curl_out_writer(char * response,size_t size,size_t nmemb,void * userdata)128 size_t curl_out_writer([[maybe_unused]] char* response, size_t size,
129 size_t nmemb, [[maybe_unused]] void* userdata) {
130 return size * nmemb;
131 }
132
SetCurlUrlPart(CURLU * url,CURLUPart part,const char * value)133 CURLUcode SetCurlUrlPart(CURLU* url, CURLUPart part, const char* value) {
134 CURLUcode urc = curl_url_set(url, part, value, 0);
135 if (urc != 0) {
136 LOG(ERROR) << "Failed to set url part '" << part << "' to '" << value
137 << "': Error '" << curl_url_strerror(urc) << "'";
138 }
139 return urc;
140 }
141
ClearcutServerUrl(metrics::ClearcutServer server)142 std::string ClearcutServerUrl(metrics::ClearcutServer server) {
143 switch (server) {
144 case metrics::kLocal:
145 return "http://localhost:27910/log";
146
147 case metrics::kStaging:
148 return "https://play.googleapis.com:443/staging/log";
149
150 case metrics::kProd:
151 return "https://play.googleapis.com:443/log";
152
153 default:
154 LOG(FATAL) << "Invalid host configuration";
155 return "";
156 }
157 }
158
PostRequest(const std::string & output,metrics::ClearcutServer server)159 MetricsExitCodes PostRequest(const std::string& output,
160 metrics::ClearcutServer server) {
161 std::string clearcut_url = ClearcutServerUrl(server);
162
163 std::unique_ptr<CURLU, void (*)(CURLU*)> url(curl_url(), curl_url_cleanup);
164 if (!url) {
165 LOG(ERROR) << "Failed to initialize CURLU.";
166 return kMetricsError;
167 }
168
169 CURLUcode urc =
170 curl_url_set(url.get(), CURLUPART_URL, clearcut_url.c_str(), 0);
171 if (urc != 0) {
172 LOG(ERROR) << "Failed to set url to " << url.get() << clearcut_url
173 << "': " << curl_url_strerror(urc) << "'";
174 return kMetricsError;
175 }
176 curl_global_init(CURL_GLOBAL_ALL);
177
178 std::unique_ptr<CURL, void (*)(CURL*)> curl(curl_easy_init(),
179 curl_easy_cleanup);
180
181 if (!curl) {
182 LOG(ERROR) << "Failed to initialize CURL.";
183 return kMetricsError;
184 }
185
186 curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, &curl_out_writer);
187 curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, 0L);
188 curl_easy_setopt(curl.get(), CURLOPT_CURLU, url.get());
189 curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, output.data());
190 curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDSIZE, output.size());
191 CURLcode rc = curl_easy_perform(curl.get());
192 long http_code = 0;
193 curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &http_code);
194
195 if (rc == CURLE_ABORTED_BY_CALLBACK || http_code != 200) {
196 LOG(ERROR) << "Metrics message failed: [" << output << "]";
197 LOG(ERROR) << "http error code: " << http_code;
198 LOG(ERROR) << "curl error code: " << rc << " | " << curl_easy_strerror(rc);
199 return kMetricsError;
200 }
201 LOG(INFO) << "Metrics posted to ClearCut";
202 curl_global_cleanup();
203 return kSuccess;
204 }
205
206 } // namespace cuttlefish::metrics
207