/* XMRig * Copyright 2010 Jeff Garzik * Copyright 2012-2014 pooler * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee * Copyright 2016-2017 XMRig * Copyright 2017- BenDr0id * * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include <3rdparty/rapidjson/stringbuffer.h> #include <3rdparty/rapidjson/prettywriter.h> #include #include "CCClient.h" #include "App.h" #include "Platform.h" #include "Cpu.h" #include "Mem.h" #include "ControlCommand.h" #include "api/NetworkState.h" #include "log/Log.h" #include "workers/Workers.h" #include "workers/Hashrate.h" #if _WIN32 # include "winsock2.h" #else # include "unistd.h" #endif CCClient* CCClient::m_self = nullptr; uv_mutex_t CCClient::m_mutex; CCClient::CCClient(Options* options, uv_async_t* async) : m_options(options), m_async(async) { uv_mutex_init(&m_mutex); m_self = this; std::string clientId; if (m_options->ccWorkerId()) { clientId = m_options->ccWorkerId(); } else { char hostname[128]; memset(hostname, 0, sizeof(hostname)); gethostname(hostname, sizeof(hostname) - 1); clientId = std::string(hostname); } m_clientStatus.setClientId(clientId); if (m_options->algoName() != nullptr) { m_clientStatus.setCurrentAlgoName(m_options->algoName()); } m_clientStatus.setHugepagesEnabled(Mem::isHugepagesEnabled()); m_clientStatus.setHugepages(Mem::isHugepagesAvailable()); m_clientStatus.setDoubleHashMode(m_options->doubleHash()); m_clientStatus.setVersion(Version::string()); m_clientStatus.setCpuBrand(Cpu::brand()); m_clientStatus.setCpuAES(Cpu::hasAES()); m_clientStatus.setCpuCores(Cpu::cores()); m_clientStatus.setCpuX64(Cpu::isX64()); m_clientStatus.setCpuL2(Cpu::l2()); m_clientStatus.setCpuL3(Cpu::l3()); m_clientStatus.setCurrentThreads(m_options->threads()); if (m_options->ccToken() != nullptr) { m_authorization = std::string("Bearer ") + m_self->m_options->ccToken(); } uv_thread_create(&m_thread, CCClient::onThreadStarted, this); } CCClient::~CCClient() { uv_timer_stop(&m_timer); m_self = nullptr; } void CCClient::updateHashrate(const Hashrate* hashrate) { if (m_self) { uv_mutex_lock(&m_mutex); m_self->m_clientStatus.setHashrateShort(hashrate->calc(Hashrate::ShortInterval)); m_self->m_clientStatus.setHashrateMedium(hashrate->calc(Hashrate::MediumInterval)); m_self->m_clientStatus.setHashrateLong(hashrate->calc(Hashrate::LargeInterval)); m_self->m_clientStatus.setHashrateHighest(hashrate->highest()); uv_mutex_unlock(&m_mutex); } } void CCClient::updateNetworkState(const NetworkState& network) { if (m_self) { uv_mutex_lock(&m_mutex); m_self->m_clientStatus.setCurrentStatus(Workers::isEnabled() ? ClientStatus::RUNNING : ClientStatus::PAUSED); m_self->m_clientStatus.setCurrentPool(network.pool); m_self->m_clientStatus.setSharesGood(network.accepted); m_self->m_clientStatus.setSharesTotal(network.accepted + network.rejected); m_self->m_clientStatus.setHashesTotal(network.total); m_self->m_clientStatus.setAvgTime(network.avgTime()); uv_mutex_unlock(&m_mutex); } } void CCClient::publishClientStatusReport() { std::string requestUrl = "/client/setClientStatus?clientId=" + m_self->m_clientStatus.getClientId(); std::string requestBuffer = m_self->m_clientStatus.toJsonString(); auto res = performRequest(requestUrl, requestBuffer, "POST"); if (!res) { LOG_ERR("[CC-Client] error: unable to performRequest POST -> http://%s:%d%s", m_self->m_options->ccHost(), m_self->m_options->ccPort(), requestUrl.c_str()); } else if (res->status != 200) { LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_self->m_options->ccHost(), m_self->m_options->ccPort(), requestUrl.c_str()); } else { ControlCommand controlCommand; if (controlCommand.parseFromJsonString(res->body)) { if (controlCommand.getCommand() == ControlCommand::START) { if (!Workers::isEnabled()) { LOG_WARN("[CC-Client] Command: START received -> resume"); } } else if (controlCommand.getCommand() == ControlCommand::STOP) { if (Workers::isEnabled()) { LOG_WARN("[CC-Client] Command: STOP received -> pause"); } } else if (controlCommand.getCommand() == ControlCommand::UPDATE_CONFIG) { LOG_WARN("[CC-Client] Command: UPDATE_CONFIG received -> update config"); updateConfig(); } else if (controlCommand.getCommand() == ControlCommand::RESTART) { LOG_WARN("[CC-Client] Command: RESTART received -> restart"); } else if (controlCommand.getCommand() == ControlCommand::SHUTDOWN) { LOG_WARN("[CC-Client] Command: SHUTDOWN received -> shutdown"); } m_self->m_async->data = reinterpret_cast(controlCommand.getCommand()); uv_async_send(m_self->m_async); } else { LOG_ERR("[CC-Client] Unknown command received from CC Server."); } } } void CCClient::updateConfig() { std::string requestUrl = "/client/getConfig?clientId=" + m_self->m_clientStatus.getClientId(); std::string requestBuffer; auto res = performRequest(requestUrl, requestBuffer, "GET"); if (!res) { LOG_ERR("[CC-Client] error: unable to performRequest GET -> http://%s:%d%s", m_self->m_options->ccHost(), m_self->m_options->ccPort(), requestUrl.c_str()); } else if (res->status != 200) { LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_self->m_options->ccHost(), m_self->m_options->ccPort(), requestUrl.c_str()); } else { rapidjson::Document document; if (!document.Parse(res->body.c_str()).HasParseError()) { std::ofstream clientConfigFile(m_self->m_options->configFile()); if (clientConfigFile) { rapidjson::StringBuffer buffer(0, 65536); rapidjson::PrettyWriter writer(buffer); writer.SetMaxDecimalPlaces(10); document.Accept(writer); clientConfigFile << buffer.GetString(); clientConfigFile.close(); LOG_WARN("[CC-Client] Config updated. -> trigger restart"); } else { LOG_ERR("[CC-Client] Not able to store client config to file %s.", m_self->m_options->configFile()); } } else { LOG_ERR("[CC-Client] Not able to store client config. received client config is broken!"); } } } std::shared_ptr CCClient::performRequest(const std::string& requestUrl, const std::string& requestBuffer, const std::string& operation) { httplib::Client cli(m_self->m_options->ccHost(), m_self->m_options->ccPort()); httplib::Request req; req.method = operation; req.path = requestUrl; req.set_header("Host", ""); req.set_header("Accept", "*/*"); req.set_header("User-Agent", Platform::userAgent()); req.set_header("Accept", "application/json"); req.set_header("Content-Type", "application/json"); if (!m_self->m_authorization.empty()) { req.set_header("Authorization", m_self->m_authorization.c_str()); } if (!requestBuffer.empty()) { req.body = requestBuffer; } auto res = std::make_shared(); return cli.send(req, *res) ? res : nullptr; } void CCClient::onThreadStarted(void* handle) { uv_loop_init(&m_self->m_client_loop); uv_timer_init(&m_self->m_client_loop, &m_self->m_timer); uv_timer_start(&m_self->m_timer, CCClient::onReport, static_cast(m_self->m_options->ccUpdateInterval() * 1000), static_cast(m_self->m_options->ccUpdateInterval() * 1000)); uv_run(&m_self->m_client_loop, UV_RUN_DEFAULT); } void CCClient::onReport(uv_timer_t* handle) { if (m_self) { m_self->publishClientStatusReport(); } }