// // Copyright (C) 2022 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common/libs/utils/tee_logging.h" #include "host/commands/metrics/metrics_defs.h" #include "host/commands/metrics/utils.h" using cuttlefish::MetricsExitCodes; namespace metrics { static std::string Hashing(const std::string& input) { const std::hash hasher; return std::to_string(hasher(input)); } std::string GetOsName() { struct utsname buf; if (uname(&buf) != 0) { LOG(ERROR) << "failed to retrieve system information"; return "Error"; } return std::string(buf.sysname); } std::string GenerateSessionId(uint64_t now_ms) { uint64_t now_day = now_ms / 1000 / 60 / 60 / 24; return Hashing(GetMacAddress() + std::to_string(now_day)); } std::string GetCfVersion() { // TODO: per ellisr@ leave empty for now return ""; } std::string GetOsVersion() { struct utsname buf; if (uname(&buf) != 0) { LOG(ERROR) << "failed to retrieve system information"; } std::string version = buf.release; return version; } std::string GetMacAddress() { int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (sock == -1) { LOG(ERROR) << "couldn't connect to socket"; return ""; } char buf2[1024]; struct ifconf ifc; ifc.ifc_len = sizeof(buf2); ifc.ifc_buf = buf2; if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { LOG(ERROR) << "couldn't connect to socket"; return ""; } struct ifreq* it = ifc.ifc_req; const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq)); unsigned char mac_address[6] = {0}; struct ifreq ifr; for (; it != end; ++it) { strcpy(ifr.ifr_name, it->ifr_name); if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) { LOG(ERROR) << "couldn't connect to socket"; return ""; } if (ifr.ifr_flags & IFF_LOOPBACK) { continue; } if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) { memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6); break; } } char mac[100]; sprintf(mac, "%02x:%02x:%02x:%02x:%02x:%02x", mac_address[0], mac_address[1], mac_address[2], mac_address[3], mac_address[4], mac_address[5]); return mac; } std::string GetCompany() { // TODO: per ellisr@ leave hard-coded for now return "GOOGLE"; } std::string GetVmmVersion() { // TODO: per ellisr@ leave empty for now return ""; } uint64_t GetEpochTimeMs() { auto now = std::chrono::system_clock::now().time_since_epoch(); uint64_t milliseconds_since_epoch = std::chrono::duration_cast(now).count(); return milliseconds_since_epoch; } size_t curl_out_writer([[maybe_unused]] char* response, size_t size, size_t nmemb, [[maybe_unused]] void* userdata) { return size * nmemb; } CURLUcode SetCurlUrlPart(CURLU* url, CURLUPart part, const char* value) { CURLUcode urc = curl_url_set(url, part, value, 0); if (urc != 0) { LOG(ERROR) << "Failed to set url part '" << part << "' to '" << value << "': Error '" << curl_url_strerror(urc) << "'"; } return urc; } std::string ClearcutServerUrl(metrics::ClearcutServer server) { switch (server) { case metrics::kLocal: return "http://localhost:27910/log"; case metrics::kStaging: return "https://play.googleapis.com:443/staging/log"; case metrics::kProd: return "https://play.googleapis.com:443/log"; default: LOG(FATAL) << "Invalid host configuration"; return ""; } } MetricsExitCodes PostRequest(const std::string& output, metrics::ClearcutServer server) { std::string clearcut_url = ClearcutServerUrl(server); std::unique_ptr url(curl_url(), curl_url_cleanup); if (!url) { LOG(ERROR) << "Failed to initialize CURLU."; return cuttlefish::kMetricsError; } CURLUcode urc = curl_url_set(url.get(), CURLUPART_URL, clearcut_url.c_str(), 0); if (urc != 0) { LOG(ERROR) << "Failed to set url to " << url.get() << clearcut_url << "': " << curl_url_strerror(urc) << "'"; return cuttlefish::kMetricsError; } curl_global_init(CURL_GLOBAL_ALL); std::unique_ptr curl(curl_easy_init(), curl_easy_cleanup); if (!curl) { LOG(ERROR) << "Failed to initialize CURL."; return cuttlefish::kMetricsError; } curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, &curl_out_writer); curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl.get(), CURLOPT_CURLU, url.get()); curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, output.data()); curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDSIZE, output.size()); CURLcode rc = curl_easy_perform(curl.get()); long http_code = 0; curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &http_code); if (rc == CURLE_ABORTED_BY_CALLBACK || http_code != 200) { LOG(ERROR) << "Metrics message failed: [" << output << "]"; LOG(ERROR) << "http error code: " << http_code; LOG(ERROR) << "curl error code: " << rc << " | " << curl_easy_strerror(rc); return cuttlefish::kMetricsError; } LOG(INFO) << "Metrics posted to ClearCut"; curl_global_cleanup(); return cuttlefish::kSuccess; } } // namespace metrics