From 28c94eac6a19fa285711a9711ce38096c670af10 Mon Sep 17 00:00:00 2001 From: BenDroid Date: Tue, 7 Nov 2017 20:11:23 +0100 Subject: [PATCH] Added xmrigCC-common src --- src/cc/CCClient.cpp | 228 +++++++++++++++++++++++++++ src/cc/CCClient.h | 71 +++++++++ src/cc/CCServer.cpp | 198 +++++++++++++++++++++++ src/cc/CCServer.h | 65 ++++++++ src/cc/ClientStatus.cpp | 279 +++++++++++++++++++++++++++++++++ src/cc/ClientStatus.h | 131 ++++++++++++++++ src/cc/ControlCommand.cpp | 99 ++++++++++++ src/cc/ControlCommand.h | 83 ++++++++++ src/cc/Httpd.cpp | 238 ++++++++++++++++++++++++++++ src/cc/Httpd.h | 66 ++++++++ src/cc/README.md | 7 + src/cc/Service.cpp | 322 ++++++++++++++++++++++++++++++++++++++ src/cc/Service.h | 68 ++++++++ src/cc/Summary.cpp | 67 ++++++++ src/cc/XMRigCC.cpp | 31 ++++ src/cc/XMRigd.cpp | 61 ++++++++ 16 files changed, 2014 insertions(+) create mode 100644 src/cc/CCClient.cpp create mode 100644 src/cc/CCClient.h create mode 100644 src/cc/CCServer.cpp create mode 100644 src/cc/CCServer.h create mode 100644 src/cc/ClientStatus.cpp create mode 100644 src/cc/ClientStatus.h create mode 100644 src/cc/ControlCommand.cpp create mode 100644 src/cc/ControlCommand.h create mode 100644 src/cc/Httpd.cpp create mode 100644 src/cc/Httpd.h create mode 100644 src/cc/README.md create mode 100644 src/cc/Service.cpp create mode 100644 src/cc/Service.h create mode 100644 src/cc/Summary.cpp create mode 100644 src/cc/XMRigCC.cpp create mode 100644 src/cc/XMRigd.cpp diff --git a/src/cc/CCClient.cpp b/src/cc/CCClient.cpp new file mode 100644 index 00000000..471690ef --- /dev/null +++ b/src/cc/CCClient.cpp @@ -0,0 +1,228 @@ +/* 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 "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(const Options *options) + : m_options(options) +{ + 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()); + } + + if (m_options->ccToken() != nullptr) { + m_authorization = std::string("Bearer ") + m_self->m_options->ccToken(); + } + + uv_timer_init(uv_default_loop(), &m_timer); + uv_timer_start(&m_timer, CCClient::onReport, + static_cast(m_options->ccUpdateInterval() * 1000), + static_cast(m_options->ccUpdateInterval() * 1000)); +} + +CCClient::~CCClient() +{ + uv_timer_stop(&m_timer); + m_self = nullptr; +} + +void CCClient::updateHashrate(const Hashrate *hashrate) +{ + uv_mutex_lock(&m_mutex); + + if (m_self) { + 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) +{ + uv_mutex_lock(&m_mutex); + + if (m_self) { + 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"); + Workers::setEnabled(true); + } + } else if (controlCommand.getCommand() == ControlCommand::STOP) { + if (Workers::isEnabled()) { + LOG_WARN("[CC-Client] Command: STOP received -> pause"); + Workers::setEnabled(false); + } + } 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"); + App::restart(); + } else if (controlCommand.getCommand() == ControlCommand::SHUTDOWN) { + LOG_WARN("[CC-Client] Command: SHUTDOWN received -> shutdown"); + App::shutdown(); + } + } 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. -> restart"); + App::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.c_str(); + req.path = requestUrl.c_str(); + 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.c_str(); + } + + auto res = std::make_shared(); + + return cli.send(req, *res) ? res : nullptr; +} + +void CCClient::onReport(uv_timer_t *handle) +{ + if (m_self) { + m_self->publishClientStatusReport(); + } +} diff --git a/src/cc/CCClient.h b/src/cc/CCClient.h new file mode 100644 index 00000000..6a947ab9 --- /dev/null +++ b/src/cc/CCClient.h @@ -0,0 +1,71 @@ +/* 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 . + */ + +#ifndef __CC_CLIENT_H__ +#define __CC_CLIENT_H__ + +#ifndef XMRIG_NO_CC + +#include +#include <3rdparty/cpp-httplib/httplib.h> +#include "Options.h" +#include "ClientStatus.h" + +class Hashrate; +class NetworkState; + +class CCClient +{ +public: + CCClient(const Options *options); + ~CCClient(); + + static void updateHashrate(const Hashrate *hashrate); + static void updateNetworkState(const NetworkState &results); + +private: + + static void publishClientStatusReport(); + static void updateConfig(); + static std::shared_ptr performRequest(const std::string& requestUrl, + const std::string& requestBuffer, + const std::string& operation); + + static void onReport(uv_timer_t *handle); + + const Options *m_options; + + static CCClient* m_self; + static uv_mutex_t m_mutex; + + ClientStatus m_clientStatus; + + std::string m_authorization; + + uv_timer_t m_timer; + +}; + +#endif +#endif /* __CC_CLIENT_H__ */ diff --git a/src/cc/CCServer.cpp b/src/cc/CCServer.cpp new file mode 100644 index 00000000..c4969853 --- /dev/null +++ b/src/cc/CCServer.cpp @@ -0,0 +1,198 @@ +/* XMRigCC + * 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 "CCServer.h" +#include "Service.h" +#include "Httpd.h" +#include "Console.h" +#include "log/ConsoleLog.h" +#include "log/FileLog.h" +#include "log/Log.h" +#include "Options.h" +#include "Summary.h" + +#if _WIN32 + #include + #include +#else +# include "unistd.h" +#endif + +#ifdef HAVE_SYSLOG_H +# include "log/SysLog.h" +#endif + +CCServer *CCServer::m_self = nullptr; + + +CCServer::CCServer(int argc, char** argv) : + m_console(nullptr), + m_httpd(nullptr), + m_options(nullptr) +{ + m_self = this; + + Log::init(); + + m_options = Options::parse(argc, argv); + if (!m_options) { + return; + } + + if (!m_options->background()) { + Log::add(new ConsoleLog(m_options->colors())); + m_console = new Console(this); + } + + if (m_options->logFile()) { + Log::add(new FileLog(m_options->logFile())); + } + +# ifdef HAVE_SYSLOG_H + if (m_options->syslog()) { + Log::add(new SysLog()); + } +# endif + + uv_signal_init(uv_default_loop(), &m_signal); +} + +CCServer::~CCServer() +{ + uv_tty_reset_mode(); + + delete m_httpd; +} + +int CCServer::start() +{ + if (!m_options) { + return 0; + } + + uv_signal_start(&m_signal, CCServer::onSignal, SIGHUP); + uv_signal_start(&m_signal, CCServer::onSignal, SIGTERM); + uv_signal_start(&m_signal, CCServer::onSignal, SIGINT); + + if (m_options->background()) { + moveToBackground(); + } + + Summary::print(); + + Service::start(); + + m_httpd = new Httpd(m_options); + m_httpd->start(); + + const int r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + uv_loop_close(uv_default_loop()); + + Options::release(); + + return r; +} + +void CCServer::onConsoleCommand(char command) +{ + switch (command) { + case 'q': + case 'Q': + stop(); + break; + + case 3: + LOG_WARN("Ctrl+C received, exiting"); + stop(); + break; + + default: + break; + } +} + +void CCServer::stop() +{ + uv_stop(uv_default_loop()); +} + +void CCServer::onSignal(uv_signal_t* handle, int signum) +{ + switch (signum) + { + case SIGHUP: + LOG_WARN("SIGHUP received, exiting"); + break; + + case SIGTERM: + LOG_WARN("SIGTERM received, exiting"); + break; + + case SIGINT: + LOG_WARN("SIGINT received, exiting"); + break; + + default: + break; + } + + uv_signal_stop(handle); + m_self->stop(); +} + +void CCServer::moveToBackground() +{ +#ifdef WIN32 + HWND hcon = GetConsoleWindow(); + if (hcon) { + ShowWindow(hcon, SW_HIDE); + } else { + HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); + CloseHandle(h); + FreeConsole(); + } +#else + int i = fork(); + if (i < 0) { + exit(1); + } + + if (i > 0) { + exit(0); + } + + i = setsid(); + + if (i < 0) { + LOG_ERR("setsid() failed (errno = %d)", errno); + } + + i = chdir("/"); + if (i < 0) { + LOG_ERR("chdir() failed (errno = %d)", errno); + } +#endif +} diff --git a/src/cc/CCServer.h b/src/cc/CCServer.h new file mode 100644 index 00000000..fb4a3daa --- /dev/null +++ b/src/cc/CCServer.h @@ -0,0 +1,65 @@ +/* XMRigCC + * 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 . + */ + +#ifndef __CC_SERVER_H__ +#define __CC_SERVER_H__ + + +#include + + +#include "interfaces/IConsoleListener.h" + + +class Console; +class Httpd; +class Options; + +class CCServer : public IConsoleListener +{ +public: + CCServer(int argc, char **argv); + ~CCServer(); + + int start(); + +protected: + void onConsoleCommand(char command) override; + +private: + void stop(); + void moveToBackground(); + + static void onSignal(uv_signal_t* handle, int signum); + + static CCServer* m_self; + + Console* m_console; + Httpd* m_httpd; + Options* m_options; + uv_signal_t m_signal; +}; + + +#endif /* __CC_SERVER_H__ */ diff --git a/src/cc/ClientStatus.cpp b/src/cc/ClientStatus.cpp new file mode 100644 index 00000000..ad9ca03b --- /dev/null +++ b/src/cc/ClientStatus.cpp @@ -0,0 +1,279 @@ +/* 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 +#include <3rdparty/rapidjson/stringbuffer.h> +#include <3rdparty/rapidjson/prettywriter.h> +#include + +#include "ClientStatus.h" + +ClientStatus::ClientStatus() + : m_hashrateShort(0), + m_hashrateMedium(0), + m_hashrateLong(0), + m_sharesGood(0), + m_sharesTotal(0), + m_hashesTotal(0), + m_lastStatusUpdate(0) +{ + +} + +std::string ClientStatus::getClientId() const +{ + return m_clientId; +} + +void ClientStatus::setClientId(const std::string& clientId) +{ + m_clientId = clientId; +} + +std::string ClientStatus::getCurrentPool() const +{ + return m_currentPool; +} + +void ClientStatus::setCurrentPool(const std::string& currentPool) +{ + m_currentPool = currentPool; +} + +void ClientStatus::setCurrentAlgoName(const std::string& algoName) +{ + m_currentAlgoName = algoName; +} + +std::string ClientStatus::getCurrentAlgoName() const +{ + return m_currentAlgoName; +} + +ClientStatus::Status ClientStatus::getCurrentStatus() const +{ + return m_currentStatus; +} + +void ClientStatus::setCurrentStatus(Status currentStatus) +{ + m_currentStatus = currentStatus; +} + +double ClientStatus::getHashrateShort() const +{ + return m_hashrateShort; +} + +void ClientStatus::setHashrateShort(double hashrateShort) +{ + m_hashrateShort = hashrateShort; +} + +double ClientStatus::getHashrateMedium() const +{ + return m_hashrateMedium; +} + +void ClientStatus::setHashrateMedium(double hashrateMedium) +{ + m_hashrateMedium = hashrateMedium; +} + +double ClientStatus::getHashrateLong() const +{ + return m_hashrateLong; +} + +void ClientStatus::setHashrateLong(double hashrateLong) +{ + m_hashrateLong = hashrateLong; +} + +uint64_t ClientStatus::getSharesGood() const +{ + return m_sharesGood; +} + +void ClientStatus::setSharesGood(uint64_t sharesGood) +{ + m_sharesGood = sharesGood; +} + +uint64_t ClientStatus::getSharesTotal() const +{ + return m_sharesTotal; +} + +void ClientStatus::setSharesTotal(uint64_t sharesTotal) +{ + m_sharesTotal = sharesTotal; +} + +uint64_t ClientStatus::getHashesTotal() const +{ + return m_hashesTotal; +} + +void ClientStatus::setHashesTotal(uint64_t hashesTotal) +{ + m_hashesTotal = hashesTotal; +} + +void ClientStatus::setHashrateHighest(double hashrateHighest) +{ + m_hashrateHighest = hashrateHighest; +} + +double ClientStatus::getHashrateHighest() const +{ + return m_hashrateHighest; +} + +void ClientStatus::setAvgTime(uint32_t avgTime) +{ + m_avgTime = avgTime; +} + +uint32_t ClientStatus::getAvgTime() const +{ + return m_avgTime; +} + +std::time_t ClientStatus::getLastStatusUpdate() const +{ + return m_lastStatusUpdate; +} + +bool ClientStatus::parseFromJson(const rapidjson::Document& document) +{ + bool result = false; + + if (document.HasMember("client_status")) + { + rapidjson::Value::ConstObject clientStatus = document["client_status"].GetObject(); + + if (clientStatus.HasMember("client_id")) { + m_clientId = clientStatus["client_id"].GetString(); + } + + if (clientStatus.HasMember("current_pool")) { + m_currentPool = clientStatus["current_pool"].GetString(); + } + + if (clientStatus.HasMember("current_status")) { + m_currentStatus = toStatus(clientStatus["current_status"].GetString()); + } + + if (clientStatus.HasMember("current_algo_name")) { + m_currentAlgoName = clientStatus["current_algo_name"].GetString(); + } + + if (clientStatus.HasMember("hashrate_short")) { + m_hashrateShort = clientStatus["hashrate_short"].GetDouble(); + } + + if (clientStatus.HasMember("hashrate_medium")) { + m_hashrateMedium = clientStatus["hashrate_medium"].GetDouble(); + } + + if (clientStatus.HasMember("hashrate_long")) { + m_hashrateLong = clientStatus["hashrate_long"].GetDouble(); + } + + if (clientStatus.HasMember("hashrate_highest")) { + m_hashrateHighest = clientStatus["hashrate_highest"].GetDouble(); + } + + if (clientStatus.HasMember("avg_time")) { + m_avgTime = clientStatus["avg_time"].GetUint(); + } + + if (clientStatus.HasMember("shares_good")) { + m_sharesGood = clientStatus["shares_good"].GetUint64(); + } + + if (clientStatus.HasMember("shares_total")) { + m_sharesTotal = clientStatus["shares_total"].GetUint64(); + } + + if (clientStatus.HasMember("hashes_total")) { + m_hashesTotal = clientStatus["hashes_total"].GetUint64(); + } + + auto time_point = std::chrono::system_clock::now(); + m_lastStatusUpdate = std::chrono::system_clock::to_time_t(time_point); + + result = true; + } else { + LOG_ERR("Parse Error, JSON does not contain: control_command"); + } + + return result; +} + +rapidjson::Value ClientStatus::toJson(rapidjson::MemoryPoolAllocator& allocator) +{ + rapidjson::Value clientStatus(rapidjson::kObjectType); + + clientStatus.AddMember("client_id", rapidjson::StringRef(m_clientId.c_str()), allocator); + clientStatus.AddMember("current_pool", rapidjson::StringRef(m_currentPool.c_str()), allocator); + clientStatus.AddMember("current_status", rapidjson::StringRef(toString(m_currentStatus)), allocator); + clientStatus.AddMember("current_algo_name", rapidjson::StringRef(m_currentAlgoName.c_str()), allocator); + + clientStatus.AddMember("hashrate_short", m_hashrateShort, allocator); + clientStatus.AddMember("hashrate_medium", m_hashrateMedium, allocator); + clientStatus.AddMember("hashrate_long", m_hashrateLong, allocator); + clientStatus.AddMember("hashrate_highest", m_hashrateHighest, allocator); + clientStatus.AddMember("avg_time", m_avgTime, allocator); + + clientStatus.AddMember("shares_good", m_sharesGood, allocator); + clientStatus.AddMember("shares_total", m_sharesTotal, allocator); + clientStatus.AddMember("hashes_total", m_hashesTotal, allocator); + + clientStatus.AddMember("last_status_update", m_lastStatusUpdate, allocator); + + return clientStatus; +} + +std::string ClientStatus::toJsonString() +{ + rapidjson::Document respDocument; + respDocument.SetObject(); + + auto& allocator = respDocument.GetAllocator(); + + rapidjson::Value clientStatus = ClientStatus::toJson(allocator); + respDocument.AddMember("client_status", clientStatus, allocator); + + rapidjson::StringBuffer buffer(0, 4096); + rapidjson::Writer writer(buffer); + writer.SetMaxDecimalPlaces(10); + respDocument.Accept(writer); + + return strdup(buffer.GetString()); +} + diff --git a/src/cc/ClientStatus.h b/src/cc/ClientStatus.h new file mode 100644 index 00000000..8ebc0a9b --- /dev/null +++ b/src/cc/ClientStatus.h @@ -0,0 +1,131 @@ +/* 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 . + */ + +#ifndef __CLIENT_STATUS_H__ +#define __CLIENT_STATUS_H__ + +#include +#include +#include "rapidjson/document.h" + +class ClientStatus +{ + +public: + enum Status { + RUNNING, + PAUSED, + CONFIG_UPDATED + }; + +public: + ClientStatus(); + + inline const char *toString (Status status) + { + return status_str[static_cast(status)]; + } + + inline Status toStatus (const char *status) + { + const int n = sizeof(status_str) / sizeof(status_str[0]); + for (int i = 0; i < n; ++i) + { + if (strcmp(status_str[i], status) == 0) + return (Status) i; + } + return Status::RUNNING; + } + + std::string getClientId() const; + void setClientId(const std::string& clientId); + + std::string getCurrentPool() const; + void setCurrentPool(const std::string& currentPool); + + std::string getCurrentAlgoName() const; + void setCurrentAlgoName(const std::string &algoName); + + Status getCurrentStatus() const; + void setCurrentStatus(Status currentStatus); + + double getHashrateShort() const; + void setHashrateShort(double hashrateShort); + + double getHashrateMedium() const; + void setHashrateMedium(double hashrateMedium); + + double getHashrateLong() const; + void setHashrateLong(double hashrateLong); + + uint64_t getSharesGood() const; + void setSharesGood(uint64_t sharesGood); + + uint64_t getSharesTotal() const; + void setSharesTotal(uint64_t sharesTotal); + + uint64_t getHashesTotal() const; + void setHashesTotal(uint64_t hashesTotal); + + void setHashrateHighest(double hashrateHighest); + double getHashrateHighest() const; + + void setAvgTime(uint32_t avgTime); + uint32_t getAvgTime() const; + + std::time_t getLastStatusUpdate() const; + + std::string toJsonString(); + rapidjson::Value toJson(rapidjson::MemoryPoolAllocator& allocator); + bool parseFromJson(const rapidjson::Document& document); + + +private: + const char* status_str[3] = { + "RUNNING", + "PAUSED", + "CONFIG_UPDATED" + }; + + Status m_currentStatus; + + std::string m_clientId; + std::string m_currentPool; + std::string m_currentAlgoName; + + double m_hashrateShort; + double m_hashrateMedium; + double m_hashrateLong; + double m_hashrateHighest; + + uint64_t m_sharesGood; + uint64_t m_sharesTotal; + uint64_t m_hashesTotal; + + uint32_t m_avgTime; + + std::time_t m_lastStatusUpdate; +}; + +#endif /* __CLIENT_STATUS_H__ */ diff --git a/src/cc/ControlCommand.cpp b/src/cc/ControlCommand.cpp new file mode 100644 index 00000000..ac191040 --- /dev/null +++ b/src/cc/ControlCommand.cpp @@ -0,0 +1,99 @@ +/* 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 <3rdparty/rapidjson/stringbuffer.h> +#include <3rdparty/rapidjson/prettywriter.h> + +#include "log/Log.h" +#include "ControlCommand.h" + +ControlCommand::ControlCommand() + : m_command(Command::START) +{ + +} + +ControlCommand::ControlCommand(Command command) + : m_command(command) +{ + +} + +bool ControlCommand::parseFromJsonString(const std::string& json) +{ + bool result = false; + + rapidjson::Document document; + if (!document.Parse(json.c_str()).HasParseError()) { + result = parseFromJson(document); + } + + return result; +} + +bool ControlCommand::parseFromJson(const rapidjson::Document& document) +{ + bool result = false; + + if (document.HasMember("control_command")) { + rapidjson::Value::ConstObject controlCommand = document["control_command"].GetObject(); + if (controlCommand.HasMember("command")) { + m_command = toCommand(controlCommand["command"].GetString()); + result = true; + } + else { + LOG_ERR("Parse Error, JSON does not contain: command"); + } + } else { + LOG_ERR("Parse Error, JSON does not contain: control_command"); + } + + return result; +} + +rapidjson::Value ControlCommand::toJson(rapidjson::MemoryPoolAllocator& allocator) +{ + rapidjson::Value controlCommand(rapidjson::kObjectType); + + controlCommand.AddMember("command", rapidjson::StringRef(toString(m_command)), allocator); + + return controlCommand; +} + +void ControlCommand::setCommand(Command command) +{ + m_command = command; +} + +ControlCommand::Command ControlCommand::getCommand() const +{ + return m_command; +} + +bool ControlCommand::isOneTimeCommand() const { + + return m_command == ControlCommand::UPDATE_CONFIG || + m_command == ControlCommand::RESTART || + m_command == ControlCommand::SHUTDOWN; +} diff --git a/src/cc/ControlCommand.h b/src/cc/ControlCommand.h new file mode 100644 index 00000000..13b9b6e3 --- /dev/null +++ b/src/cc/ControlCommand.h @@ -0,0 +1,83 @@ +/* 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 . + */ + +#ifndef __CONTROL_COMMAND_H__ +#define __CONTROL_COMMAND_H__ + +#include +#include "rapidjson/document.h" + +class ControlCommand +{ +public: + enum Command { + START, + STOP, + UPDATE_CONFIG, + RESTART, + SHUTDOWN + }; + +public: + ControlCommand(); + explicit ControlCommand(Command command); + + inline const char *toString (Command command) + { + return command_str[static_cast(command)]; + } + + inline Command toCommand (const char *command) + { + const int n = sizeof(command_str) / sizeof(command_str[0]); + for (int i = 0; i < n; ++i) + { + if (strcmp(command_str[i], command) == 0) + return (Command) i; + } + return Command::START; + } + + rapidjson::Value toJson(rapidjson::MemoryPoolAllocator& allocator); + bool parseFromJsonString(const std::string& json); + bool parseFromJson(const rapidjson::Document& document); + + Command getCommand() const; + void setCommand(Command command); + + bool isOneTimeCommand() const; + +private: + const char* command_str[5] = { + "START", + "STOP", + "UPDATE_CONFIG", + "RESTART", + "SHUTDOWN" + }; + + Command m_command; +}; + +#endif /* __CONTROL_COMMAND_H__ */ diff --git a/src/cc/Httpd.cpp b/src/cc/Httpd.cpp new file mode 100644 index 00000000..6b0a95a1 --- /dev/null +++ b/src/cc/Httpd.cpp @@ -0,0 +1,238 @@ +/* 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 +#include + +#include "version.h" +#include "Service.h" +#include "Httpd.h" +#include "log/Log.h" + +Httpd::Httpd(const Options *options) + : m_options(options) + , m_daemon(nullptr) +{ +} + +bool Httpd::start() +{ + if (!m_options->ccPort()) { + return false; + } + + m_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, static_cast(m_options->ccPort()), nullptr, nullptr, &Httpd::handler, + this, MHD_OPTION_END); + + if (!m_daemon) { + LOG_ERR("HTTP Daemon failed to start."); + return false; + } else { + LOG_INFO("%s Server started on Port: %d", APP_NAME, m_options->ccPort()); + } + + return true; +} + +unsigned Httpd::tokenAuth(struct MHD_Connection* connection) +{ + if (!m_options->ccToken()) { + LOG_WARN("AccessToken not set. Access Granted!"); + return MHD_HTTP_OK; + } + + const char* header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_AUTHORIZATION); + if (m_options->ccToken() && !header) { + return MHD_HTTP_UNAUTHORIZED; + } + + const size_t size = strlen(header); + if (size < 8 || strlen(m_options->ccToken()) != size - 7 || memcmp("Bearer ", header, 7) != 0) { + LOG_WARN("AccessToken wrong. Access Forbidden!"); + return MHD_HTTP_FORBIDDEN; + } + + return strncmp(m_options->ccToken(), header + 7, strlen(m_options->ccToken())) == 0 ? MHD_HTTP_OK : MHD_HTTP_FORBIDDEN; +} + +unsigned Httpd::basicAuth(struct MHD_Connection* connection, std::string& resp) +{ + if (!m_options->ccAdminUser() || !m_options->ccAdminPass()) { + resp = std::string("" + "Please configure admin user and pass to view this Page." + ""); + + LOG_WARN("Admin user/password not set. Access Forbidden!"); + return MHD_HTTP_FORBIDDEN; + } + + const char* header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_AUTHORIZATION); + if (!header) { + return MHD_HTTP_UNAUTHORIZED; + } + + char* user; + char* pass; + + user = MHD_basic_auth_get_username_password(connection, &pass); + + if (user == nullptr || strcmp(user, m_options->ccAdminUser()) != 0 || + pass == nullptr || strcmp(pass, m_options->ccAdminPass()) != 0) { + + LOG_WARN("Admin user/password wrong. Access Unauthorized!"); + return MHD_HTTP_UNAUTHORIZED; + } + + return MHD_HTTP_OK; +} + +int Httpd::sendResponse(MHD_Connection* connection, unsigned status, MHD_Response* rsp, const char* contentType) +{ + if (!rsp) { + rsp = MHD_create_response_from_buffer(0, nullptr, MHD_RESPMEM_MUST_COPY); + } + + MHD_add_response_header(rsp, "Content-Type", contentType); + MHD_add_response_header(rsp, "Access-Control-Allow-Origin", "*"); + MHD_add_response_header(rsp, "Access-Control-Allow-Methods", "POST, GET, OPTIONS"); + MHD_add_response_header(rsp, "Access-Control-Allow-Headers", "Content-Type, Authorization"); + MHD_add_response_header(rsp, "WWW-Authenticate", "Basic"); + MHD_add_response_header(rsp, "WWW-Authenticate", "Bearer"); + + int ret = MHD_queue_response(connection, status, rsp); + + MHD_destroy_response(rsp); + + return ret; +} + + +int Httpd::handler(void* httpd, MHD_Connection* connection, const char* url, const char* method, + const char* version, const char* upload_data, size_t* upload_data_size, void** con_cls) +{ + if (strcmp(method, MHD_HTTP_METHOD_OPTIONS) == 0) { + LOG_INFO("OPTIONS Requested"); + return sendResponse(connection, MHD_HTTP_OK, nullptr, CONTENT_TYPE_HTML); + } + + if (strcmp(method, MHD_HTTP_METHOD_GET) != 0 && strcmp(method, MHD_HTTP_METHOD_POST) != 0) { + LOG_ERR("HTTP_METHOD_NOT_ALLOWED"); + return sendResponse(connection, MHD_HTTP_METHOD_NOT_ALLOWED, nullptr, CONTENT_TYPE_HTML); + } + + if (strstr(url, "/client/")) { + unsigned status = static_cast(httpd)->tokenAuth(connection); + if (status != MHD_HTTP_OK) { + return sendResponse(connection, status, nullptr, CONTENT_TYPE_JSON); + } + } else { + std::string resp; + unsigned status = static_cast(httpd)->basicAuth(connection, resp); + if (status != MHD_HTTP_OK) { + MHD_Response* rsp = nullptr; + if (!resp.empty()) { + rsp = MHD_create_response_from_buffer(resp.length(), (void*)resp.c_str(), MHD_RESPMEM_MUST_COPY); + } + return sendResponse(connection, status, rsp, CONTENT_TYPE_HTML); + } + } + + if (strcmp(method, MHD_HTTP_METHOD_GET) == 0) { + return handleGET(static_cast(httpd), connection, url); + } else { + return handlePOST(static_cast(httpd), connection, url, upload_data, upload_data_size, con_cls); + } + + return MHD_NO; +} + +int Httpd::handleGET(const Httpd* httpd, struct MHD_Connection* connection, const char* urlPtr) +{ + std::string resp; + std::string url(urlPtr); + std::string clientId; + + const char* clientIdPtr = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "clientId"); + if (clientIdPtr) + { + clientId = std::string(clientIdPtr); + } + + unsigned status = Service::handleGET(httpd->m_options, url, clientId, resp); + + MHD_Response* rsp = nullptr; + if (!resp.empty()) { + rsp = MHD_create_response_from_buffer(resp.length(), (void*) resp.c_str(), MHD_RESPMEM_MUST_COPY); + } + + char* contentType; + if (url == "/") { + contentType = const_cast(CONTENT_TYPE_HTML); + } else { + contentType = const_cast(CONTENT_TYPE_JSON); + } + + return sendResponse(connection, status, rsp, contentType); +} + +int Httpd::handlePOST(const Httpd* httpd, struct MHD_Connection* connection, const char* urlPtr, const char* upload_data, + size_t* upload_data_size, void** con_cls) +{ + auto* cc = (ConnectionContext*)* con_cls; + if (cc == nullptr) { + cc = new ConnectionContext(); + *con_cls = (void*) cc; + } else { + if (*upload_data_size != 0) { + cc->data << std::string(upload_data, *upload_data_size); + + *upload_data_size = 0; + } else { + std::string resp; + std::string url(urlPtr); + std::string clientId; + + const char* clientIdPtr = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "clientId"); + if (clientIdPtr) + { + clientId = std::string(clientIdPtr); + } + + unsigned status = Service::handlePOST(httpd->m_options, url, clientId, cc->data.str(), resp); + + MHD_Response* rsp = nullptr; + if (!resp.empty()) { + rsp = MHD_create_response_from_buffer(resp.length(), (void*) resp.c_str(), MHD_RESPMEM_MUST_COPY); + } + + delete cc; + *con_cls = nullptr; + + return sendResponse(connection, status, rsp, CONTENT_TYPE_JSON); + } + } + + return MHD_YES; +} diff --git a/src/cc/Httpd.h b/src/cc/Httpd.h new file mode 100644 index 00000000..95d5ecc8 --- /dev/null +++ b/src/cc/Httpd.h @@ -0,0 +1,66 @@ +/* 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 . + */ + +#ifndef __HTTPD_H__ +#define __HTTPD_H__ + +#include +#include +#include + +#include "Options.h" + + +struct MHD_Connection; +struct MHD_Daemon; +struct MHD_Response; + +class Httpd +{ +public: + Httpd(const Options *options); + bool start(); + +private: + + typedef struct PostContext + { + std::stringstream data; + } ConnectionContext; + + static int sendResponse(MHD_Connection* connection, unsigned status, MHD_Response* rsp, const char* contentType); + + unsigned basicAuth(MHD_Connection* connection, std::string &resp); + unsigned tokenAuth(MHD_Connection* connection); + + static int handler(void* httpd, MHD_Connection* connection, const char* url, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, void**con_cls); + static int handleGET(const Httpd* httpd, MHD_Connection* connection, const char* url); + static int handlePOST(const Httpd* httpd, MHD_Connection* connection, const char* url, const char* upload_data, size_t* upload_data_size, void** con_cls); + + const Options* m_options; + MHD_Daemon* m_daemon; + +}; + +#endif /* __HTTPD_H__ */ diff --git a/src/cc/README.md b/src/cc/README.md new file mode 100644 index 00000000..f414dfa6 --- /dev/null +++ b/src/cc/README.md @@ -0,0 +1,7 @@ +# XMRigCC common repository +This repository contains all common XMRigCC files. + +# Used in this repos +* [xmrigCC](https://github.com/bendr0id/xmrigCC) +* [xmrigCC-nvidia](https://github.com/bendr0id/xmrigCC-nvidia) + diff --git a/src/cc/Service.cpp b/src/cc/Service.cpp new file mode 100644 index 00000000..f8a2ca7e --- /dev/null +++ b/src/cc/Service.cpp @@ -0,0 +1,322 @@ +/* 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 +#include +#include <3rdparty/rapidjson/document.h> +#include <3rdparty/rapidjson/stringbuffer.h> +#include <3rdparty/rapidjson/writer.h> +#include <3rdparty/rapidjson/filewritestream.h> +#include <3rdparty/rapidjson/filereadstream.h> +#include <3rdparty/rapidjson/error/en.h> +#include <3rdparty/rapidjson/prettywriter.h> +#include "log/Log.h" +#include "Service.h" + +uv_mutex_t Service::m_mutex; +std::map Service::m_clientCommand; +std::map Service::m_clientStatus; + +bool Service::start() +{ + uv_mutex_init(&m_mutex); + + return true; +} + +void Service::release() +{ + uv_mutex_lock(&m_mutex); + + m_clientCommand.clear(); + m_clientStatus.clear(); + + uv_mutex_unlock(&m_mutex); +} + +unsigned Service::handleGET(const Options* options, const std::string& url, const std::string& clientId, std::string& resp) +{ + uv_mutex_lock(&m_mutex); + + unsigned resultCode = MHD_HTTP_NOT_FOUND; + + LOG_INFO("GET(url='%s', clientId='%s')", url.c_str(), clientId.c_str()); + + if (url == "/") { + resultCode = getAdminPage(options, resp); + } else if (url.rfind("/admin/getClientStatusList", 0) == 0) { + resultCode = getClientStatusList(resp); + } else { + if (!clientId.empty()) { + if (url.rfind("/client/getConfig", 0) == 0 || url.rfind("/admin/getClientConfig", 0) == 0) { + resultCode = getClientConfig(options, clientId, resp); + } else if (url.rfind("/admin/getClientCommand", 0) == 0) { + resultCode = getClientCommand(clientId, resp); + } + } + else { + LOG_ERR("Request does not contain clientId: %s", url.c_str()); + } + } + + uv_mutex_unlock(&m_mutex); + + return resultCode; +} + +unsigned Service::handlePOST(const Options* options, const std::string& url, const std::string& clientId, const std::string& data, std::string& resp) +{ + uv_mutex_lock(&m_mutex); + + unsigned resultCode = MHD_HTTP_NOT_FOUND; + + LOG_INFO("POST(url='%s', clientId='%s', dataLen='%d')", url.c_str(), clientId.c_str(), data.length()); + + if (url.rfind("/client/setClientStatus", 0) == 0) { + resultCode = setClientStatus(clientId, data, resp); + } else if (url.rfind("/admin/setClientConfig", 0) == 0) { + resultCode = setClientConfig(options, clientId, data, resp); + } else if (url.rfind("/admin/setClientCommand", 0) == 0) { + resultCode = setClientCommand(clientId, data, resp); + } + + uv_mutex_unlock(&m_mutex); + + return resultCode; +} + +unsigned Service::getClientConfig(const Options* options, const std::string& clientId, std::string& resp) +{ + unsigned resultCode = MHD_HTTP_INTERNAL_SERVER_ERROR; + + std::string clientConfigFileName = getClientConfigFileName(options, clientId); + + std::stringstream data; + std::ifstream clientConfig(clientConfigFileName); + if (clientConfig) { + data << clientConfig.rdbuf(); + clientConfig.close(); + } else { + std::ifstream defaultConfig("default_config.json"); + if (defaultConfig) { + data << defaultConfig.rdbuf(); + defaultConfig.close(); + } + } + + if (data.tellp() > 0) { + rapidjson::Document document; + document.Parse(data.str().c_str()); + + if (!document.HasParseError()) { + rapidjson::StringBuffer buffer(0, 65536); + rapidjson::Writer writer(buffer); + writer.SetMaxDecimalPlaces(10); + document.Accept(writer); + + resp = strdup(buffer.GetString()); + + resultCode = MHD_HTTP_OK; + } else { + LOG_ERR("Not able to send client config. Client config %s is broken!", clientConfigFileName.c_str()); + } + } else{ + LOG_ERR("Not able to load a client config. Please check your configuration!"); + } + + return resultCode; +} + +unsigned Service::setClientConfig(const Options* options, const std::string &clientId, const std::string &data, std::string &resp) +{ + unsigned resultCode = MHD_HTTP_BAD_REQUEST; + + rapidjson::Document document; + if (!document.Parse(data.c_str()).HasParseError()) { + std::string clientConfigFileName = getClientConfigFileName(options, clientId); + std::ofstream clientConfigFile(clientConfigFileName); + + if (clientConfigFile){ + rapidjson::StringBuffer buffer(0, 4096); + rapidjson::PrettyWriter writer(buffer); + writer.SetMaxDecimalPlaces(10); + document.Accept(writer); + + clientConfigFile << buffer.GetString(); + clientConfigFile.close(); + + resultCode = MHD_HTTP_OK; + } else { + LOG_ERR("Not able to store client config to file %s.", clientConfigFileName.c_str()); + } + } else{ + LOG_ERR("Not able to store client config. The received client config for client %s is broken!", clientId.c_str()); + } + + return resultCode; +} + +unsigned Service::getClientStatusList(std::string& resp) +{ + rapidjson::Document document; + document.SetObject(); + + auto& allocator = document.GetAllocator(); + + rapidjson::Value clientStatusList(rapidjson::kArrayType); + for (auto& clientStatus : m_clientStatus) { + rapidjson::Value clientStatusEntry(rapidjson::kObjectType); + clientStatusEntry.AddMember("client_status", clientStatus.second.toJson(allocator), allocator); + clientStatusList.PushBack(clientStatusEntry, allocator); + } + + document.AddMember("client_status_list", clientStatusList, allocator); + + rapidjson::StringBuffer buffer(0, 4096); + rapidjson::Writer writer(buffer); + writer.SetMaxDecimalPlaces(10); + document.Accept(writer); + + resp = strdup(buffer.GetString()); + + return MHD_HTTP_OK; +} + +unsigned Service::setClientStatus(const std::string& clientId, const std::string& data, std::string& resp) +{ + int resultCode = MHD_HTTP_BAD_REQUEST; + + rapidjson::Document document; + if (!document.Parse(data.c_str()).HasParseError()) { + LOG_INFO("Status from client: %s", clientId.c_str()); + + ClientStatus clientStatus; + clientStatus.parseFromJson(document); + + m_clientStatus[clientId] = clientStatus; + + resultCode = getClientCommand(clientId, resp); + + if (m_clientCommand[clientId].isOneTimeCommand()) { + m_clientCommand.erase(clientId); + } + } else { + LOG_ERR("Parse Error Occured: %d", document.GetParseError()); + } + + return resultCode; +} + +unsigned Service::getClientCommand(const std::string& clientId, std::string& resp) +{ + if (m_clientCommand.find(clientId) == m_clientCommand.end()) { + m_clientCommand[clientId] = ControlCommand(); + } + + rapidjson::Document respDocument; + respDocument.SetObject(); + + auto& allocator = respDocument.GetAllocator(); + + rapidjson::Value controlCommand = m_clientCommand[clientId].toJson(allocator); + respDocument.AddMember("control_command", controlCommand, allocator); + + rapidjson::StringBuffer buffer(0, 4096); + rapidjson::Writer writer(buffer); + writer.SetMaxDecimalPlaces(10); + respDocument.Accept(writer); + + resp = strdup(buffer.GetString()); + + return MHD_HTTP_OK; +} + +unsigned Service::setClientCommand(const std::string& clientId, const std::string& data, std::string& resp) +{ + ControlCommand controlCommand; + + rapidjson::Document document; + if (!document.Parse(data.c_str()).HasParseError()) { + controlCommand.parseFromJson(document); + + m_clientCommand[clientId] = controlCommand; + + return MHD_HTTP_OK; + } else { + return MHD_HTTP_BAD_REQUEST; + } +} + +unsigned Service::getAdminPage(const Options* options, std::string& resp) +{ + std::stringstream data; + + if (options->ccCustomDashboard() != nullptr) { + std::ifstream customDashboard(options->ccCustomDashboard()); + if (customDashboard) + { + data << customDashboard.rdbuf(); + customDashboard.close(); + resp = data.str(); + } + } + + if (resp.empty()) { + data << ""; + data << ""; + data << ""; + data << ""; + data << "XMRigCC Dashboard"; + data << ""; + data << ""; + data << "
"; + data << "

Please configure a Dashboard

"; + data << "
"; + data << ""; + data << ""; + } + + resp = data.str(); + + return MHD_HTTP_OK; +} + +std::string Service::getClientConfigFileName(const Options* options, const std::string& clientId) +{ + std::string clientConfigFileName; + + if (options->ccClientConfigFolder() != nullptr) { + clientConfigFileName += options->ccClientConfigFolder(); +# ifdef WIN32 + clientConfigFileName += '\\'; +# else + clientConfigFileName += '/'; +# endif + } + + clientConfigFileName += clientId + std::string("_config.json"); + + return clientConfigFileName; +} diff --git a/src/cc/Service.h b/src/cc/Service.h new file mode 100644 index 00000000..a80c9387 --- /dev/null +++ b/src/cc/Service.h @@ -0,0 +1,68 @@ +/* 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 . + */ + +#ifndef __SERVICE_H__ +#define __SERVICE_H__ + +#define CONTENT_TYPE_HTML "text/html" +#define CONTENT_TYPE_JSON "application/json" + +#include +#include +#include +#include +#include "Options.h" +#include "ClientStatus.h" +#include "ControlCommand.h" + +class Service +{ +public: + static bool start(); + static void release(); + + static unsigned handleGET(const Options* options, const std::string& url, const std::string& clientId, std::string& resp); + static unsigned handlePOST(const Options* options, const std::string& url, const std::string& clientId, const std::string& data, std::string& resp); + +private: + static unsigned getClientConfig(const Options* options, const std::string& clientId, std::string& resp); + static unsigned getClientCommand(const std::string& clientId, std::string& resp); + static unsigned getClientStatusList(std::string& resp); + static unsigned getAdminPage(const Options* options, std::string& resp); + + static unsigned setClientStatus(const std::string& clientId, const std::string& data, std::string& resp); + static unsigned setClientCommand(const std::string& clientId, const std::string& data, std::string& resp); + static unsigned setClientConfig(const Options* options, const std::string &clientId, const std::string &data, std::string &resp); + + static std::string getClientConfigFileName(const Options *options, const std::string &clientId); + +private: + static std::map m_clientStatus; + static std::map m_clientCommand; + + static uv_mutex_t m_mutex; + +}; + +#endif /* __SERVICE_H__ */ diff --git a/src/cc/Summary.cpp b/src/cc/Summary.cpp new file mode 100644 index 00000000..f6a13d3b --- /dev/null +++ b/src/cc/Summary.cpp @@ -0,0 +1,67 @@ + +/* 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 "log/Log.h" +#include "Options.h" +#include "Summary.h" +#include "version.h" + +static void print_versions() +{ + char buf[16]; + +# if defined(__clang__) + snprintf(buf, 16, " clang/%d.%d.%d", __clang_major__, __clang_minor__, __clang_patchlevel__); +# elif defined(__GNUC__) + snprintf(buf, 16, " gcc/%d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); +# elif defined(_MSC_VER) + snprintf(buf, 16, " MSVC/%d", MSVC_VERSION); +# else + buf[0] = '\0'; +# endif + + + Log::i()->text(Options::i()->colors() ? "\x1B[01;32m * \x1B[01;37mVERSIONS: \x1B[01;36m%s/%s\x1B[01;37m libuv/%s%s" : " * VERSIONS: %s/%s libuv/%s%s", + APP_NAME, APP_VERSION, uv_version_string(), buf); +} + +static void print_commands() +{ + if (Options::i()->colors()) { + Log::i()->text("\x1B[01;32m * \x1B[01;37mCOMMANDS: \x1B[01;35mq\x1B[01;37muit"); + } + else { + Log::i()->text(" * COMMANDS: 'q' Quit"); + } +} + + +void Summary::print() +{ + print_versions(); + print_commands(); +} diff --git a/src/cc/XMRigCC.cpp b/src/cc/XMRigCC.cpp new file mode 100644 index 00000000..356b2cf2 --- /dev/null +++ b/src/cc/XMRigCC.cpp @@ -0,0 +1,31 @@ +/* XMRigCC + * 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 "CCServer.h" + + +int main(int argc, char** argv) { + CCServer ccServer(argc, argv); + return ccServer.start(); +} diff --git a/src/cc/XMRigd.cpp b/src/cc/XMRigd.cpp new file mode 100644 index 00000000..eed49a4f --- /dev/null +++ b/src/cc/XMRigd.cpp @@ -0,0 +1,61 @@ +/* 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 + +#ifdef WIN32 + #define WIN32_LEAN_AND_MEAN /* avoid including junk */ + #include + #include +#endif +int main(int argc, char **argv) { + + std::string ownPath(argv[0]); + std::string xmrigDaemon("xmrigDaemon"); + std::string xmrigMiner("xmrigMiner"); + + std::string xmrigMinerPath = ownPath.replace(ownPath.rfind(xmrigDaemon),xmrigDaemon.size(), xmrigMiner); + +#ifdef WIN32 + xmrigMinerPath = "\"" + xmrigMinerPath + "\""; +#endif + + for (int i=1; i < argc; i++){ + xmrigMinerPath += " "; + xmrigMinerPath += argv[i]; + } + + xmrigMinerPath += " --daemonized"; + + int status = 0; + + do { + status = system(xmrigMinerPath.c_str()); +#ifdef WIN32 + } while (status == EINTR); +#else + } while (WEXITSTATUS(status) == EINTR); +#endif +}