From 215efcabb8a0b9bfc4464ec6a56cefcbab9d8d4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Mon, 16 Oct 2017 21:38:00 +0200 Subject: [PATCH] Implemented CCClient and extended CCServer service --- .gitignore | 1 + CMakeLists.txt | 59 +++-- cmake/FindMHD.cmake | 2 +- src/App.cpp | 36 ++- src/App.h | 5 +- src/Options.cpp | 188 ++++++++++++--- src/Options.h | 65 ++++-- src/Platform.cpp | 4 + src/Summary.cpp | 14 ++ src/cc/CCClient.cpp | 219 +++++++++++++++++ src/cc/CCClient.h | 71 ++++++ src/{server => cc}/CCServer.cpp | 32 +-- src/{server => cc}/CCServer.h | 11 +- src/cc/ClientStatus.cpp | 265 +++++++++++++++++++++ src/{server => cc}/ClientStatus.h | 38 +-- src/{server => cc}/ControlCommand.cpp | 57 +++-- src/{server => cc}/ControlCommand.h | 12 +- src/cc/Httpd.cpp | 238 +++++++++++++++++++ src/{server => cc}/Httpd.h | 27 +-- src/cc/Service.cpp | 325 ++++++++++++++++++++++++++ src/{server => cc}/Service.h | 21 +- src/{server => cc}/Summary.cpp | 7 +- src/{server => cc}/xmrigCC.cpp | 2 +- src/net/Network.cpp | 6 + src/net/strategies/DonateStrategy.cpp | 2 +- src/server/ClientStatus.cpp | 145 ------------ src/server/Httpd.cpp | 235 ------------------- src/server/Service.cpp | 82 ------- src/server/version.h | 57 ----- src/version.h | 7 + src/workers/Workers.cpp | 6 + 31 files changed, 1538 insertions(+), 701 deletions(-) create mode 100644 src/cc/CCClient.cpp create mode 100644 src/cc/CCClient.h rename src/{server => cc}/CCServer.cpp (89%) rename src/{server => cc}/CCServer.h (89%) create mode 100644 src/cc/ClientStatus.cpp rename src/{server => cc}/ClientStatus.h (70%) rename src/{server => cc}/ControlCommand.cpp (60%) rename src/{server => cc}/ControlCommand.h (84%) create mode 100644 src/cc/Httpd.cpp rename src/{server => cc}/Httpd.h (57%) create mode 100644 src/cc/Service.cpp rename src/{server => cc}/Service.h (54%) rename src/{server => cc}/Summary.cpp (92%) rename src/{server => cc}/xmrigCC.cpp (97%) delete mode 100644 src/server/ClientStatus.cpp delete mode 100644 src/server/Httpd.cpp delete mode 100644 src/server/Service.cpp delete mode 100644 src/server/version.h diff --git a/.gitignore b/.gitignore index dede7979..ca5bcc52 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /.idea /CMakeFiles /src/3rdparty +/cmake-build-debug CMakeCache.txt cmake_install.cmake Makefile diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e8e1cf7..6c4d3611 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,8 @@ set(CMAKE_BUILD_TYPE Debug) option(WITH_LIBCPUID "Use Libcpuid" ON) option(WITH_AEON "CryptoNight-Lite support" ON) -option(WITH_HTTPD "HTTP REST API" ON) -option(WITH_CC_SERVER "CC Server" ON) +option(WITH_HTTPD "HTTP REST API" OFF) +option(WITH_CC "CC Server" ON) include (CheckIncludeFile) @@ -237,13 +237,13 @@ else() add_definitions(/DXMRIG_NO_API) endif() -if (WITH_CC_SERVER) +if (WITH_CC) find_package(MHD) if (MHD_FOUND) include_directories(${MHD_INCLUDE_DIRS}) else() - message(FATAL_ERROR "microhttpd NOT found: use `-DWITH_CC_SERVER=OFF` to build without CC Server support") + message(FATAL_ERROR "microhttpd NOT found: use `-DWITH_CC=OFF` to build without CC Server support") endif() find_package(CURL) @@ -251,37 +251,48 @@ if (WITH_CC_SERVER) if (CURL_FOUND) include_directories(${CURL_INCLUDE_DIRS}) else() - message(FATAL_ERROR "microhttpd NOT found: use `-DWITH_CC_SERVER=OFF` to build without CC Server support") + message(FATAL_ERROR "microhttpd NOT found: use `-DWITH_CC=OFF` to build without CC Server support") endif() - set(CC_SERVER_SOURCES - src/server/ControlCommand.cpp - src/server/ClientStatus.cpp - src/server/xmrigCC.cpp - src/server/CCServer.cpp - src/server/Service.cpp - src/server/Summary.cpp - src/server/Httpd.cpp) + set(HEADERS_CC_COMMON + src/cc/ControlCommand.h + src/cc/ClientStatus.h) - set(CC_SERVER_HEADERS - src/server/ControlCommand.h - src/server/ClientStatus.h - src/server/CCServer.h - src/server/Service.h - src/server/version.h - src/server/Httpd.h) + set(SOURCES_CC_COMMON + src/cc/ControlCommand.cpp + src/cc/ClientStatus.cpp) + + set(HEADERS_CC_CLIENT + src/cc/CCClient.h) + + set(SOURCES_CC_CLIENT + src/cc/CCClient.cpp) + + set(HEADERS_CC_SERVER + src/cc/CCServer.h + src/cc/Service.h + src/cc/Httpd.h) + + set(SOURCES_CC_SERVER + src/cc/CCServer.cpp + src/cc/xmrigCC.cpp + src/cc/Service.cpp + src/cc/Summary.cpp + src/cc/Httpd.cpp) else() - add_definitions(/DXMRIG_NO_CC_SERVER) + add_definitions(/DXMRIG_NO_CC) endif() include_directories(src) include_directories(src/3rdparty) include_directories(${UV_INCLUDE_DIR}) -add_executable(xmrig ${HEADERS} ${SOURCES} ${HEADERS_COMMON} ${SOURCES_COMMON} ${SOURCES_OS} ${SOURCES_CPUID} ${HEADERS_CRYPTO} ${SOURCES_CRYPTO} ${SOURCES_SYSLOG} ${HTTPD_SOURCES}) +add_executable(xmrig ${HEADERS} ${SOURCES} ${HEADERS_COMMON} ${SOURCES_COMMON} ${SOURCES_OS} ${SOURCES_CPUID} ${HEADERS_CRYPTO} ${SOURCES_CRYPTO} ${SOURCES_SYSLOG} ${HTTPD_SOURCES} ${SOURCES_CC_COMMON} ${HEADERS_CC_COMMON} ${SOURCES_CC_CLIENT} ${HEADERS_CC_CLIENT}) target_link_libraries(xmrig ${UV_LIBRARIES} ${MHD_LIBRARY} ${CURL_LIBRARY} ${EXTRA_LIBS} ${CPUID_LIB}) -if (WITH_CC_SERVER AND MHD_FOUND AND CURL_FOUND) - add_executable(xmrigCC ${HEADERS_COMMON} ${SOURCES_COMMON} ${SOURCES_OS} ${SOURCES_CPUID} ${HEADERS_CC_SERVER} ${CC_SERVER_SOURCES} ${SOURCES_SYSLOG}) + +if (WITH_CC AND MHD_FOUND AND CURL_FOUND) + add_executable(xmrigCC ${HEADERS_COMMON} ${SOURCES_COMMON} ${SOURCES_OS} ${SOURCES_CPUID} ${SOURCES_SYSLOG} ${SOURCES_CC_COMMON} ${HEADERS_CC_COMMON} ${SOURCES_CC_SERVER} ${HEADERS_CC_SERVER}) target_link_libraries(xmrigCC ${UV_LIBRARIES} ${MHD_LIBRARY} ${EXTRA_LIBS} ${CPUID_LIB}) + set_target_properties(xmrigCC PROPERTIES COMPILE_FLAGS "-DXMRIG_CC_SERVER ${SHARED_FLAGS}") endif() \ No newline at end of file diff --git a/cmake/FindMHD.cmake b/cmake/FindMHD.cmake index 8505b337..f6665892 100644 --- a/cmake/FindMHD.cmake +++ b/cmake/FindMHD.cmake @@ -24,7 +24,7 @@ set(MHD_LIBRARIES ${MHD_LIBRARY}) # same naming convention as in qt (appending debug library with d) # boost is using the same "hack" as us with "optimized" and "debug" # official MHD project actually uses _d suffix -if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC) +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL MSVC) find_library( MHD_LIBRARY_DEBUG NAMES microhttpd_d microhttpd-10_d libmicrohttpd_d libmicrohttpd-dll_d diff --git a/src/App.cpp b/src/App.cpp index 1aae7ae0..8ca983c6 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -5,6 +5,7 @@ * 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 @@ -25,7 +26,6 @@ #include #include - #include "api/Api.h" #include "App.h" #include "Console.h" @@ -41,7 +41,7 @@ #include "Summary.h" #include "version.h" #include "workers/Workers.h" - +#include "cc/CCClient.h" #ifdef HAVE_SYSLOG_H # include "log/SysLog.h" @@ -60,7 +60,8 @@ App::App(int argc, char **argv) : m_console(nullptr), m_httpd(nullptr), m_network(nullptr), - m_options(nullptr) + m_options(nullptr), + m_ccclient(nullptr) { m_self = this; @@ -104,6 +105,10 @@ App::~App() delete m_httpd; # endif +# ifndef XMRIG_NO_CC + delete m_ccclient; +# endif + delete m_console; } @@ -137,6 +142,10 @@ int App::exec() m_httpd->start(); # endif +# ifndef XMRIG_NO_CC + m_ccclient = new CCClient(m_options); +# endif + Workers::start(m_options->affinity(), m_options->priority()); m_network->connect(); @@ -195,6 +204,26 @@ void App::close() uv_stop(uv_default_loop()); } +void App::reloadConfig() +{ + // reload config + m_self->m_options->parseConfig(m_self->m_options->configFile()); + + Platform::release(); + Platform::init(m_self->m_options->userAgent()); + Platform::setProcessPriority(m_self->m_options->priority()); + + m_self->m_network->stop(); + Workers::stop(); // free resources here + + Mem::release(); + Mem::allocate(m_self->m_options->algo(), m_self->m_options->threads(), m_self->m_options->doubleHash(), m_self->m_options->hugePages()); + Summary::print(); + + Workers::start(m_self->m_options->affinity(), m_self->m_options->priority()); + + m_self->m_options +} void App::onSignal(uv_signal_t *handle, int signum) { @@ -219,3 +248,4 @@ void App::onSignal(uv_signal_t *handle, int signum) uv_signal_stop(handle); m_self->close(); } + diff --git a/src/App.h b/src/App.h index 781f78f2..c08f5fcf 100644 --- a/src/App.h +++ b/src/App.h @@ -35,7 +35,7 @@ class Console; class Httpd; class Network; class Options; - +class CCClient; class App : public IConsoleListener { @@ -45,6 +45,8 @@ public: int exec(); + static void reloadConfig(); + protected: void onConsoleCommand(char command) override; @@ -60,6 +62,7 @@ private: Httpd *m_httpd; Network *m_network; Options *m_options; + CCClient *m_ccclient; uv_signal_t m_signal; }; diff --git a/src/Options.cpp b/src/Options.cpp index b1197223..3e8d2771 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -5,6 +5,7 @@ * 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 @@ -54,40 +55,60 @@ Options *Options::m_self = nullptr; static char const usage[] = "\ Usage: " APP_ID " [OPTIONS]\n\ -Options:\n\ - -a, --algo=ALGO cryptonight (default) or cryptonight-lite\n\ - -o, --url=URL URL of mining server\n\ - -O, --userpass=U:P username:password pair for mining server\n\ - -u, --user=USERNAME username for mining server\n\ - -p, --pass=PASSWORD password for mining server\n\ - -t, --threads=N number of miner threads\n\ - -v, --av=N algorithm variation, 0 auto select\n\ - -k, --keepalive send keepalived for prevent timeout (need pool support)\n\ - -r, --retries=N number of times to retry before switch to backup server (default: 5)\n\ - -R, --retry-pause=N time to pause between retries (default: 5)\n\ - --cpu-affinity set process affinity to CPU core(s), mask 0x3 for cores 0 and 1\n\ - --cpu-priority set process priority (0 idle, 2 normal to 5 highest)\n\ - --no-huge-pages disable huge pages support\n\ - --no-color disable colored output\n\ - --donate-level=N donate level, default 5%% (5 minutes in 100 minutes)\n\ - --user-agent set custom user-agent string for pool\n\ - -B, --background run the miner in the background\n\ - -c, --config=FILE load a JSON-format configuration file\n\ - -l, --log-file=FILE log all output to a file\n" -# ifdef HAVE_SYSLOG_H +Options:\n" +# ifndef XMRIG_CC_SERVER "\ - -S, --syslog use system log for output messages\n" + -a, --algo=ALGO cryptonight (default) or cryptonight-lite\n\ + -o, --url=URL URL of mining server\n\ + -O, --userpass=U:P username:password pair for mining server\n\ + -u, --user=USERNAME username for mining server\n\ + -p, --pass=PASSWORD password for mining server\n\ + -t, --threads=N number of miner threads\n\ + -v, --av=N algorithm variation, 0 auto select\n\ + -k, --keepalive send keepalived for prevent timeout (need pool support)\n\ + -r, --retries=N number of times to retry before switch to backup server (default: 5)\n\ + -R, --retry-pause=N time to pause between retries (default: 5)\n\ + --cpu-affinity set process affinity to CPU core(s), mask 0x3 for cores 0 and 1\n\ + --cpu-priority set process priority (0 idle, 2 normal to 5 highest)\n\ + --no-huge-pages disable huge pages support\n\ + --donate-level=N donate level, default 5%% (5 minutes in 100 minutes)\n\ + --user-agent set custom user-agent string for pool\n\ + --max-cpu-usage=N maximum CPU usage for automatic threads mode (default 75)\n\ + --safe safe adjust threads and av settings for current CPU\n\ + --nicehash enable nicehash/xmrig-proxy support\n\ + --print-time=N print hashrate report every N seconds\n\ + --api-port=N port for the miner API\n\ + --api-access-token=T access token for API\n\ + --api-worker-id=ID custom worker-id for API\n" +# ifndef XMRIG_NO_CC +"\ + --cc-url=URL url of the CC Server\n\ + --cc-access-token=T access token for CC Server\n\ + --cc-worker-id=ID custom worker-id for CC Server\n" # endif +# endif + +# ifdef XMRIG_CC_SERVER "\ - --max-cpu-usage=N maximum CPU usage for automatic threads mode (default 75)\n\ - --safe safe adjust threads and av settings for current CPU\n\ - --nicehash enable nicehash/xmrig-proxy support\n\ - --print-time=N print hashrate report every N seconds\n\ - --api-port=N port for the miner API\n\ - --api-access-token=T access token for API\n\ - --api-worker-id=ID custom worker-id for API\n\ - -h, --help display this help and exit\n\ - -V, --version output version information and exit\n\ + --cc-user=USERNAME CC Server admin user\n\ + --cc-pass=PASSWORD CC Server admin pass\n\ + --cc-access-token=T CC Server access token for CC Client\n\ + --cc-port=N CC Server\n\ + --cc-client-config-folder=FOLDER Folder contains the client config files\n\ + --cc-custom-dashboard=FILE loads a custom dashboard and serve it to '/'\n" +# endif +"\ + --no-color disable colored output\n" +# ifdef HAVE_SYSLOG_H +"\ + -S, --syslog use system log for output messages\n" +# endif +"\ + -B, --background run the miner in the background\n\ + -c, --config=FILE load a JSON-format configuration file\n\ + -l, --log-file=FILE log all output to a file\n\ + -h, --help display this help and exit\n\ + -V, --version output version information and exit\n\ "; @@ -124,6 +145,14 @@ static struct option const options[] = { { "api-port", 1, nullptr, 4000 }, { "api-access-token", 1, nullptr, 4001 }, { "api-worker-id", 1, nullptr, 4002 }, + { "cc-url", 1, nullptr, 4003 }, + { "cc-access-token", 1, nullptr, 4004 }, + { "cc-worker-id", 1, nullptr, 4005 }, + { "cc-port", 1, nullptr, 4006 }, + { "cc-user", 1, nullptr, 4007 }, + { "cc-pass", 1, nullptr, 4008 }, + { "cc-client-config-folder", 1, nullptr, 4009 }, + { "cc-custom-dashboard", 1, nullptr, 4010 }, { 0, 0, 0, 0 } }; @@ -169,6 +198,23 @@ static struct option const api_options[] = { }; +static struct option const cc_client_options[] = { + { "url", 1, nullptr, 4003 }, + { "access-token", 1, nullptr, 4004 }, + { "worker-id", 1, nullptr, 4005 }, + { 0, 0, 0, 0 } +}; + +static struct option const cc_server_options[] = { + { "port", 1, nullptr, 4006 }, + { "access-token", 1, nullptr, 4004 }, + { "user", 1, nullptr, 4007 }, + { "pass", 1, nullptr, 4008 }, + { "client-config-folder", 1, nullptr, 4009 }, + { "custom-dashboard", 1, nullptr, 4010 }, + { 0, 0, 0, 0 } +}; + static const char *algo_names[] = { "cryptonight", # ifndef XMRIG_NO_AEON @@ -204,10 +250,18 @@ Options::Options(int argc, char **argv) : m_ready(false), m_safe(false), m_syslog(false), + m_configFile(Platform::defaultConfigName()), m_apiToken(nullptr), m_apiWorkerId(nullptr), m_logFile(nullptr), m_userAgent(nullptr), + m_ccUrl(nullptr), + m_ccToken(nullptr), + m_ccWorkerId(nullptr), + m_ccAdminUser(nullptr), + m_ccAdminPass(nullptr), + m_ccClientConfigFolder(nullptr), + m_ccCustomDashboard(nullptr), m_algo(0), m_algoVariant(0), m_apiPort(0), @@ -218,6 +272,7 @@ Options::Options(int argc, char **argv) : m_retries(5), m_retryPause(5), m_threads(0), + m_ccPort(0), m_affinity(-1L) { m_pools.push_back(new Url()); @@ -240,6 +295,17 @@ Options::Options(int argc, char **argv) : return; } + +#ifdef XMRIG_CC_SERVER + if (m_ccPort == 0) { + parseConfig(Platform::defaultConfigName()); + } + + if (m_ccPort == 0) { + fprintf(stderr, "No CC Server Port supplied. Exiting.\n"); + return; + } +#else if (!m_pools[0]->isValid()) { parseConfig(Platform::defaultConfigName()); } @@ -253,6 +319,7 @@ Options::Options(int argc, char **argv) : if (m_algoVariant == AV2_AESNI_DOUBLE || m_algoVariant == AV4_SOFT_AES_DOUBLE) { m_doubleHash = true; } +#endif if (!m_threads) { m_threads = Cpu::optimalThreadsCount(m_algo, m_doubleHash, m_maxCpuUsage); @@ -364,6 +431,41 @@ bool Options::parseArg(int key, const char *arg) m_apiWorkerId = strdup(arg); break; + case 4003: /* --cc-url */ + free(m_ccUrl); + m_ccUrl = strdup(arg); + break; + + case 4004: /* --cc-access-token */ + free(m_ccToken); + m_ccToken = strdup(arg); + break; + + case 4005: /* --cc-worker-id */ + free(m_ccWorkerId); + m_ccWorkerId = strdup(arg); + break; + + case 4007: /* --cc-user */ + free(m_ccAdminUser); + m_ccAdminUser = strdup(arg); + break; + + case 4008: /* --cc-pass */ + free(m_ccAdminPass); + m_ccAdminPass = strdup(arg); + break; + + case 4009: /* --cc-client-config-folder */ + free(m_ccClientConfigFolder); + m_ccClientConfigFolder = strdup(arg); + break; + + case 4010: /* --cc-custom-dashboard */ + free(m_ccCustomDashboard); + m_ccCustomDashboard = strdup(arg); + break; + case 'r': /* --retries */ case 'R': /* --retry-pause */ case 'v': /* --av */ @@ -373,6 +475,8 @@ bool Options::parseArg(int key, const char *arg) case 1021: /* --cpu-priority */ case 4000: /* --api-port */ return parseArg(key, strtol(arg, nullptr, 10)); + case 4006: /* --cc-port */ + return parseArg(key, strtol(arg, nullptr, 10)); case 'B': /* --background */ case 'k': /* --keepalive */ @@ -507,6 +611,12 @@ bool Options::parseArg(int key, uint64_t arg) } break; + case 4006: /* --cc-port */ + if (arg <= 65536) { + m_ccPort = (int) arg; + } + break; + default: break; } @@ -574,6 +684,8 @@ Url *Options::parseUrl(const char *arg) const void Options::parseConfig(const char *fileName) { + m_configFile = fileName; + rapidjson::Document doc; if (!getJSON(fileName, doc)) { return; @@ -602,6 +714,20 @@ void Options::parseConfig(const char *fileName) parseJSON(&api_options[i], api); } } + + const rapidjson::Value &ccClient = doc["cc-client"]; + if (ccClient.IsObject()) { + for (size_t i = 0; i < ARRAY_SIZE(cc_client_options); i++) { + parseJSON(&cc_client_options[i], ccClient); + } + } + + const rapidjson::Value &ccServer = doc["cc-server"]; + if (ccServer.IsObject()) { + for (size_t i = 0; i < ARRAY_SIZE(cc_server_options); i++) { + parseJSON(&cc_server_options[i], ccServer); + } + } } diff --git a/src/Options.h b/src/Options.h index 06b86f38..1498259c 100644 --- a/src/Options.h +++ b/src/Options.h @@ -5,6 +5,7 @@ * 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 @@ -56,28 +57,39 @@ public: static inline Options* i() { return m_self; } static Options *parse(int argc, char **argv); - inline bool background() const { return m_background; } - inline bool colors() const { return m_colors; } - inline bool doubleHash() const { return m_doubleHash; } - inline bool hugePages() const { return m_hugePages; } - inline bool syslog() const { return m_syslog; } - inline const char *apiToken() const { return m_apiToken; } - inline const char *apiWorkerId() const { return m_apiWorkerId; } - inline const char *logFile() const { return m_logFile; } - inline const char *userAgent() const { return m_userAgent; } - inline const std::vector &pools() const { return m_pools; } - inline int algo() const { return m_algo; } - inline int algoVariant() const { return m_algoVariant; } - inline int apiPort() const { return m_apiPort; } - inline int donateLevel() const { return m_donateLevel; } - inline int printTime() const { return m_printTime; } - inline int priority() const { return m_priority; } - inline int retries() const { return m_retries; } - inline int retryPause() const { return m_retryPause; } - inline int threads() const { return m_threads; } - inline int64_t affinity() const { return m_affinity; } + inline bool background() const { return m_background; } + inline bool colors() const { return m_colors; } + inline bool doubleHash() const { return m_doubleHash; } + inline bool hugePages() const { return m_hugePages; } + inline bool syslog() const { return m_syslog; } + inline const char *configFile() const { return m_configFile; } + inline const char *apiToken() const { return m_apiToken; } + inline const char *apiWorkerId() const { return m_apiWorkerId; } + inline const char *logFile() const { return m_logFile; } + inline const char *userAgent() const { return m_userAgent; } + inline const char *ccUrl() const { return m_ccUrl; } + inline const char *ccToken() const { return m_ccToken; } + inline const char *ccWorkerId() const { return m_ccWorkerId; } + inline const char *ccAdminUser() const { return m_ccAdminUser; } + inline const char *ccAdminPass() const { return m_ccAdminPass; } + inline const char *ccClientConfigFolder() const { return m_ccClientConfigFolder; } + inline const char *ccCustomDashboard() const { return m_ccCustomDashboard; } + inline const std::vector &pools() const { return m_pools; } + inline int algo() const { return m_algo; } + inline int algoVariant() const { return m_algoVariant; } + inline int apiPort() const { return m_apiPort; } + inline int donateLevel() const { return m_donateLevel; } + inline int printTime() const { return m_printTime; } + inline int priority() const { return m_priority; } + inline int retries() const { return m_retries; } + inline int retryPause() const { return m_retryPause; } + inline int threads() const { return m_threads; } + inline int ccPort() const { return m_ccPort; } + inline int64_t affinity() const { return m_affinity; } - inline static void release() { delete m_self; } + inline static void release() { delete m_self; } + + void parseConfig(const char *fileName); const char *algoName() const; @@ -94,7 +106,6 @@ private: bool parseArg(int key, uint64_t arg); bool parseBoolean(int key, bool enable); Url *parseUrl(const char *arg) const; - void parseConfig(const char *fileName); void parseJSON(const struct option *option, const rapidjson::Value &object); void showUsage(int status) const; void showVersion(void); @@ -106,6 +117,8 @@ private: int getAlgoVariantLite() const; # endif + const char* m_configFile; + bool m_background; bool m_colors; bool m_doubleHash; @@ -117,6 +130,13 @@ private: char *m_apiWorkerId; char *m_logFile; char *m_userAgent; + char *m_ccUrl; + char *m_ccToken; + char *m_ccWorkerId; + char *m_ccAdminUser; + char *m_ccAdminPass; + char *m_ccClientConfigFolder; + char *m_ccCustomDashboard; int m_algo; int m_algoVariant; int m_apiPort; @@ -127,6 +147,7 @@ private: int m_retries; int m_retryPause; int m_threads; + int m_ccPort; int64_t m_affinity; std::vector m_pools; }; diff --git a/src/Platform.cpp b/src/Platform.cpp index 4ddb1429..cb4ad3a0 100644 --- a/src/Platform.cpp +++ b/src/Platform.cpp @@ -53,7 +53,11 @@ const char *Platform::defaultConfigName() # endif if (p) { +#ifdef XMRIG_CC_SERVER + strcpy(p + 1, "config_cc.json"); +#else strcpy(p + 1, "config.json"); +#endif return m_defaultConfigName; } } diff --git a/src/Summary.cpp b/src/Summary.cpp index 69deda70..39ce5abf 100644 --- a/src/Summary.cpp +++ b/src/Summary.cpp @@ -138,6 +138,16 @@ static void print_api() } #endif +#ifndef XMRIG_NO_CC +static void print_cc() +{ + if (Options::i()->ccUrl() == nullptr) { + return; + } + + Log::i()->text(Options::i()->colors() ? "\x1B[01;32m * \x1B[01;37mCC Server: \x1B[01;36m%s" : " * CC Server: %s", Options::i()->ccUrl()); +} +#endif static void print_commands() { @@ -162,5 +172,9 @@ void Summary::print() print_api(); # endif +# ifndef XMRIG_NO_CC + print_cc(); +# endif + print_commands(); } diff --git a/src/cc/CCClient.cpp b/src/cc/CCClient.cpp new file mode 100644 index 00000000..9a312fab --- /dev/null +++ b/src/cc/CCClient.cpp @@ -0,0 +1,219 @@ +/* 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 "CCClient.h" +#include "App.h" +#include "ControlCommand.h" + +#include "api/NetworkState.h" +#include "log/Log.h" +#include "workers/Workers.h" +#include "workers/Hashrate.h" + +CCClient *CCClient::m_self = nullptr; +uv_mutex_t CCClient::m_mutex; + +CCClient::CCClient(const Options *options) + : m_options(options) +{ + 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); + m_serverURL = std::string("http://") + options->ccUrl(); + + if (m_options->ccToken() != nullptr) { + m_authorization = std::string("Authorization: Bearer ") + m_self->m_options->ccToken(); + } + + uv_timer_init(uv_default_loop(), &m_timer); + uv_timer_start(&m_timer, CCClient::onReport, kTickInterval, kTickInterval); +} + +CCClient::~CCClient() +{ + uv_timer_stop(&m_timer); + m_self = nullptr; +} + +void CCClient::updateHashrate(const Hashrate *hashrate) +{ + 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) +{ + uv_mutex_lock(&m_mutex); + + m_self->m_clientStatus.setCurrentStatus(Workers::isEnabled() ? "mining" : "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 = m_self->m_serverURL + "/client/setClientStatus?clientId=" + m_self->m_clientStatus.getClientId(); + std::string requestBuffer = m_self->m_clientStatus.toJsonString(); + std::string responseBuffer; + + CURLcode res = performCurl(requestUrl, requestBuffer, "POST", responseBuffer); + if (res != CURLE_OK) { + LOG_ERR("CCClient error: %s", curl_easy_strerror(res)); + } else { + ControlCommand controlCommand; + if (controlCommand.parseFromJsonString(responseBuffer)) { + if (controlCommand.getCommand() == ControlCommand::START) { + if (!Workers::isEnabled()) { + LOG_INFO("Command: START received -> Resuming"); + Workers::setEnabled(true); + } + } else if (controlCommand.getCommand() == ControlCommand::STOP) { + if (!Workers::isEnabled()) { + LOG_INFO("Command: STOP received -> Pausing"); + Workers::setEnabled(true); + } + } else if (controlCommand.getCommand() == ControlCommand::UPDATE_CONFIG) { + LOG_INFO("Command: UPDATE_CONFIG received -> Updating config"); + updateConfig(); + } else if (controlCommand.getCommand() == ControlCommand::RESTART) { + LOG_INFO("Command: RESTART received -> Restart"); + App::restart(); + } else { + LOG_ERR("Command: GET_CONFIG received -> NOT IMPLEMENTED YET!"); + } + } else { + LOG_ERR("Unknown Command received from CC Server."); + } + } +} + +void CCClient::updateConfig() +{ + std::string requestUrl = m_self->m_serverURL + "/client/getConfig?clientId=" + m_self->m_clientStatus.getClientId(); + std::string requestBuffer; + std::string responseBuffer; + + CURLcode res = performCurl(requestUrl, requestBuffer, "GET", responseBuffer); + if (res != CURLE_OK) { + LOG_ERR("CCClient error: %s", curl_easy_strerror(res)); + } else { + rapidjson::Document document; + if (!document.Parse(responseBuffer.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_INFO("Config update done. Restarting."); + App::restart(); + } else { + LOG_ERR("Not able to store client config to file %s.", m_self->m_options->configFile()); + } + } else{ + LOG_ERR("Not able to store client config. The received client config is broken!"); + } + } +} + +CURLcode CCClient::performCurl(const std::string& requestUrl, const std::string& requestBuffer, + const std::string& operation, std::string& responseBuffer) +{ + curl_global_init(CURL_GLOBAL_ALL); + CURL *curl = curl_easy_init(); + + struct curl_slist *headers = nullptr; + headers = curl_slist_append(headers, "Accept: application/json"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + + if (!m_self->m_authorization.empty()) { + headers = curl_slist_append(headers, m_self->m_authorization.c_str()); + } + + if (!requestBuffer.empty()) { + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, requestBuffer.c_str()); + } + + curl_easy_setopt(curl, CURLOPT_URL, requestUrl.c_str()); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, operation.c_str()); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &CCClient::onResponse); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseBuffer); + + CURLcode res = curl_easy_perform(curl); + + curl_easy_cleanup(curl); + curl_global_cleanup(); + + return res; +} + +void CCClient::onReport(uv_timer_t *handle) +{ + m_self->publishClientStatusReport(); +} + +int CCClient::onResponse(char* data, size_t size, size_t nmemb, std::string* responseBuffer) +{ + int result = 0; + + if (responseBuffer != nullptr) { + responseBuffer->append(data, size * nmemb); + result = size * nmemb; + } + + return result; +} + diff --git a/src/cc/CCClient.h b/src/cc/CCClient.h new file mode 100644 index 00000000..e4b20178 --- /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__ + + +#include +#include +#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 CURLcode performCurl(const std::string& requestUrl, const std::string& requestBuffer, const std::string& operation, + std::string& responseBuffer); + + static void onReport(uv_timer_t *handle); + static int onResponse(char* data, size_t size, size_t nmemb, std::string* responseBuffer); + + constexpr static int kTickInterval = 10 * 1000; + const Options *m_options; + + static CCClient* m_self; + static uv_mutex_t m_mutex; + + ClientStatus m_clientStatus; + + std::string m_serverURL; + std::string m_authorization; + + uv_timer_t m_timer; + +}; + +#endif /* __CC_CLIENT_H__ */ diff --git a/src/server/CCServer.cpp b/src/cc/CCServer.cpp similarity index 89% rename from src/server/CCServer.cpp rename to src/cc/CCServer.cpp index 872fde3d..c2d08c45 100644 --- a/src/server/CCServer.cpp +++ b/src/cc/CCServer.cpp @@ -24,27 +24,24 @@ #include -#include "server/Service.h" #include "CCServer.h" +#include "cc/Service.h" +#include "cc/Httpd.h" #include "Console.h" #include "log/ConsoleLog.h" #include "log/FileLog.h" #include "log/Log.h" #include "Options.h" #include "Summary.h" -#include "server/Httpd.h" #ifdef HAVE_SYSLOG_H # include "log/SysLog.h" #endif - - CCServer *CCServer::m_self = nullptr; - -CCServer::CCServer(int argc, char **argv) : +CCServer::CCServer(int argc, char** argv) : m_console(nullptr), m_httpd(nullptr), m_options(nullptr) @@ -74,10 +71,8 @@ CCServer::CCServer(int argc, char **argv) : # endif uv_signal_init(uv_default_loop(), &m_signal); - } - CCServer::~CCServer() { uv_tty_reset_mode(); @@ -86,7 +81,6 @@ CCServer::~CCServer() delete m_console; } - int CCServer::start() { if (!m_options) { @@ -101,7 +95,7 @@ int CCServer::start() Service::start(); - m_httpd = new Httpd(m_options->apiPort(), m_options->apiToken()); + m_httpd = new Httpd(m_options); m_httpd->start(); const int r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); @@ -112,20 +106,12 @@ int CCServer::start() return r; } - void CCServer::onConsoleCommand(char command) { switch (command) { - case 'c': - case 'C': - break; - - case 'h': - case 'H': - break; - - case 'r': - case 'R': + case 'q': + case 'Q': + stop(); break; case 3: @@ -138,14 +124,12 @@ void CCServer::onConsoleCommand(char command) } } - void CCServer::stop() { uv_stop(uv_default_loop()); } - -void CCServer::onSignal(uv_signal_t *handle, int signum) +void CCServer::onSignal(uv_signal_t* handle, int signum) { switch (signum) { diff --git a/src/server/CCServer.h b/src/cc/CCServer.h similarity index 89% rename from src/server/CCServer.h rename to src/cc/CCServer.h index a882c287..d96a28a9 100644 --- a/src/server/CCServer.h +++ b/src/cc/CCServer.h @@ -49,15 +49,14 @@ protected: private: void stop(); - void printCommands(); - static void onSignal(uv_signal_t *handle, int signum); + static void onSignal(uv_signal_t* handle, int signum); - static CCServer *m_self; + static CCServer* m_self; - Console *m_console; - Httpd *m_httpd; - Options *m_options; + Console* m_console; + Httpd* m_httpd; + Options* m_options; uv_signal_t m_signal; }; diff --git a/src/cc/ClientStatus.cpp b/src/cc/ClientStatus.cpp new file mode 100644 index 00000000..bb3b9f8a --- /dev/null +++ b/src/cc/ClientStatus.cpp @@ -0,0 +1,265 @@ +/* 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 "cc/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) +{ + +} + +const std::string ClientStatus::getClientId() const +{ + return m_clientId; +} + +void ClientStatus::setClientId(const std::string& clientId) +{ + m_clientId = clientId; +} + +const std::string ClientStatus::getCurrentPool() const +{ + return m_currentPool; +} + +void ClientStatus::setCurrentPool(const std::string& currentPool) +{ + m_currentPool = currentPool; +} + +const std::string ClientStatus::getCurrentStatus() const +{ + return m_currentStatus; +} + +void ClientStatus::setCurrentStatus(const std::string& 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 sharedTotal) +{ + m_sharesTotal = sharedTotal; +} + +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 = clientStatus["current_status"].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("shared_good")) { + m_sharesGood = clientStatus["shared_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(m_currentStatus.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("shared_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/server/ClientStatus.h b/src/cc/ClientStatus.h similarity index 70% rename from src/server/ClientStatus.h rename to src/cc/ClientStatus.h index 80adfe45..7d26cd34 100644 --- a/src/server/ClientStatus.h +++ b/src/cc/ClientStatus.h @@ -26,6 +26,7 @@ #define __CLIENT_STATUS_H__ #include +#include #include "rapidjson/document.h" class ClientStatus @@ -34,14 +35,14 @@ public: ClientStatus(); - const std::string getMiner() const; - void setMiner(const std::string &miner); + const std::string getClientId() const; + void setClientId(const std::string& clientId); const std::string getCurrentPool() const; - void setCurrentPool(const std::string ¤tPool); + void setCurrentPool(const std::string& currentPool); const std::string getCurrentStatus() const; - void setCurrentStatus(const std::string ¤tStatus); + void setCurrentStatus(const std::string& currentStatus); double getHashrateShort() const; void setHashrateShort(double hashrateShort); @@ -55,31 +56,42 @@ public: uint64_t getSharesGood() const; void setSharesGood(uint64_t sharesGood); - uint64_t getSharedTotal() const; - void setSharedTotal(uint64_t sharedTotal); + uint64_t getSharesTotal() const; + void setSharesTotal(uint64_t sharesTotal); uint64_t getHashesTotal() const; void setHashesTotal(uint64_t hashesTotal); - const uint32_t getLastStatusUpdate() const; - void setLastStatusUpdate(uint32_t lastStatusUpdate); + 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); - std::string toJson(); - void parseFromJson(const rapidjson::Document &document); private: - std::string m_miner; + std::string m_clientId; std::string m_currentPool; std::string m_currentStatus; double m_hashrateShort; double m_hashrateMedium; double m_hashrateLong; + double m_hashrateHighest; uint64_t m_sharesGood; - uint64_t m_sharedTotal; + uint64_t m_sharesTotal; uint64_t m_hashesTotal; - uint32_t m_lastStatusUpdate; + + uint32_t m_avgTime; + + std::time_t m_lastStatusUpdate; }; #endif /* __CLIENT_STATUS_H__ */ diff --git a/src/server/ControlCommand.cpp b/src/cc/ControlCommand.cpp similarity index 60% rename from src/server/ControlCommand.cpp rename to src/cc/ControlCommand.cpp index 7aaf0ebb..38e790f9 100644 --- a/src/server/ControlCommand.cpp +++ b/src/cc/ControlCommand.cpp @@ -22,12 +22,11 @@ * along with this program. If not, see . */ -#include #include <3rdparty/rapidjson/stringbuffer.h> #include <3rdparty/rapidjson/prettywriter.h> #include "log/Log.h" -#include "server/ControlCommand.h" +#include "ControlCommand.h" ControlCommand::ControlCommand() : m_command(Command::START) @@ -41,49 +40,45 @@ ControlCommand::ControlCommand(ControlCommand::Command command) } -bool ControlCommand::parseFromJson(const std::string &json) +bool ControlCommand::parseFromJsonString(const std::string& json) { bool result = false; rapidjson::Document document; if (!document.Parse(json.c_str()).HasParseError()) { - if (document.HasMember("control_command")) - { - rapidjson::Value controlCommand = document["control_command"].GetObject(); - if (controlCommand.HasMember("command")) { - m_command = static_cast(controlCommand["command"].GetUint()); - result = true; - } - else { - LOG_ERR("Parse Error, JSON does not contain: command"); - } - } else { - LOG_ERR("Parse Error, JSON does not contain: control_command"); - } - } - else { - LOG_ERR("Parse Error Occured: %d", document.GetParseError()); + result = parseFromJson(document); } return result; } -std::string ControlCommand::toJson() +bool ControlCommand::parseFromJson(const rapidjson::Document& document) { - rapidjson::Document document; - document.SetObject(); + bool result = false; + if (document.HasMember("control_command")) { + rapidjson::Value::ConstObject controlCommand = document["control_command"].GetObject(); + if (controlCommand.HasMember("command")) { + m_command = static_cast(controlCommand["command"].GetUint()); + 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", m_command, document.GetAllocator()); - document.AddMember("control_command", controlCommand, document.GetAllocator()); + controlCommand.AddMember("command", m_command, allocator); - rapidjson::StringBuffer buffer(0, 1024); - rapidjson::PrettyWriter writer(buffer); - writer.SetMaxDecimalPlaces(10); - document.Accept(writer); - - return strdup(buffer.GetString());; + return controlCommand; } void ControlCommand::setCommand(ControlCommand::Command command) @@ -94,4 +89,4 @@ void ControlCommand::setCommand(ControlCommand::Command command) ControlCommand::Command ControlCommand::getCommand() const { return m_command; -} +} \ No newline at end of file diff --git a/src/server/ControlCommand.h b/src/cc/ControlCommand.h similarity index 84% rename from src/server/ControlCommand.h rename to src/cc/ControlCommand.h index dc965a16..a1655287 100644 --- a/src/server/ControlCommand.h +++ b/src/cc/ControlCommand.h @@ -26,6 +26,7 @@ #define __CONTROL_COMMAND_H__ #include +#include #include "rapidjson/document.h" class ControlCommand @@ -35,17 +36,20 @@ public: { START, STOP, - UPDATE_CONFIG + RESTART, + UPDATE_CONFIG, + HALT }; ControlCommand(); explicit ControlCommand(Command command); - bool parseFromJson(const std::string &json); - std::string toJson(); + rapidjson::Value toJson(rapidjson::MemoryPoolAllocator& allocator); + bool parseFromJsonString(const std::string& json); + bool parseFromJson(const rapidjson::Document& document); - void setCommand(Command command); Command getCommand() const; + void setCommand(Command command); private: Command m_command; diff --git a/src/cc/Httpd.cpp b/src/cc/Httpd.cpp new file mode 100644 index 00000000..55b1222c --- /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 "cc/Service.h" +#include "cc/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/server/Httpd.h b/src/cc/Httpd.h similarity index 57% rename from src/server/Httpd.h rename to src/cc/Httpd.h index 43ce0125..95d5ecc8 100644 --- a/src/server/Httpd.h +++ b/src/cc/Httpd.h @@ -5,6 +5,7 @@ * 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 @@ -28,16 +29,17 @@ #include #include +#include "Options.h" + struct MHD_Connection; struct MHD_Daemon; struct MHD_Response; - class Httpd { public: - Httpd(int port, const char *accessToken); + Httpd(const Options *options); bool start(); private: @@ -47,22 +49,17 @@ private: std::stringstream data; } ConnectionContext; - static int sendHTMLResponse(MHD_Connection *connection, unsigned status, MHD_Response *rsp); - static int sendJSONResponse(MHD_Connection *connection, unsigned status, MHD_Response *rsp); - static int sendResponse(MHD_Connection *connection, unsigned status, MHD_Response *rsp, const char *contentType); + 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); + unsigned basicAuth(MHD_Connection* connection, std::string &resp); + unsigned tokenAuth(MHD_Connection* connection); - static int handler(void *cls, 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(MHD_Connection *connection, const char *url); - static int handlePOST(MHD_Connection *connection, const char *url, const char *upload_data, size_t *upload_data_size, void **con_cls); + 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 char *m_accessToken; - const char *m_adminUser; - const char *m_adminPassword; - const int m_port; - MHD_Daemon *m_daemon; + const Options* m_options; + MHD_Daemon* m_daemon; }; diff --git a/src/cc/Service.cpp b/src/cc/Service.cpp new file mode 100644 index 00000000..04dd068c --- /dev/null +++ b/src/cc/Service.cpp @@ -0,0 +1,325 @@ +/* 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_BAD_REQUEST; + + 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); + if (url.rfind("/client/getConfig", 0) == 0) { + std::map::iterator iter = m_clientCommand.find(clientId); + if (iter != m_clientCommand.end()) { + m_clientCommand.erase(iter); + } + } + } 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_BAD_REQUEST; + + LOG_INFO("POST(url='%s', clientId='%s', data='%s')", url.c_str(), clientId.c_str(), data.c_str()); + + 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::Writer 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) +{ + 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; + } else { + LOG_ERR("Parse Error Occured: %d", document.GetParseError()); + return MHD_HTTP_BAD_REQUEST; + } + + if (m_clientCommand.find(clientId) != m_clientCommand.end()) { + m_clientCommand[clientId] = ControlCommand(); + } + + return getClientCommand(clientId, resp); +} + +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 << "Work-In-Progress"; + 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; +} \ No newline at end of file diff --git a/src/server/Service.h b/src/cc/Service.h similarity index 54% rename from src/server/Service.h rename to src/cc/Service.h index 5eb96c9d..a80c9387 100644 --- a/src/server/Service.h +++ b/src/cc/Service.h @@ -5,6 +5,7 @@ * 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 @@ -24,11 +25,14 @@ #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" @@ -38,14 +42,27 @@ public: static bool start(); static void release(); - static unsigned get(const std::string &url, std::string &resp); - static unsigned post(const std::string &url, const std::string &data, std::string &resp); + 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/server/Summary.cpp b/src/cc/Summary.cpp similarity index 92% rename from src/server/Summary.cpp rename to src/cc/Summary.cpp index c723b88a..f6a13d3b 100644 --- a/src/server/Summary.cpp +++ b/src/cc/Summary.cpp @@ -6,6 +6,7 @@ * 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 @@ -51,10 +52,10 @@ static void print_versions() static void print_commands() { if (Options::i()->colors()) { - Log::i()->text("\x1B[01;32m * \x1B[01;37mCOMMANDS: \x1B[01;35mm\x1B[01;37miners"); + Log::i()->text("\x1B[01;32m * \x1B[01;37mCOMMANDS: \x1B[01;35mq\x1B[01;37muit"); } else { - Log::i()->text(" * COMMANDS: 'm' miners"); + Log::i()->text(" * COMMANDS: 'q' Quit"); } } @@ -62,5 +63,5 @@ static void print_commands() void Summary::print() { print_versions(); - //print_commands(); + print_commands(); } diff --git a/src/server/xmrigCC.cpp b/src/cc/xmrigCC.cpp similarity index 97% rename from src/server/xmrigCC.cpp rename to src/cc/xmrigCC.cpp index 593ad716..29a4e7f6 100644 --- a/src/server/xmrigCC.cpp +++ b/src/cc/xmrigCC.cpp @@ -25,7 +25,7 @@ #include "CCServer.h" -int main(int argc, char **argv) { +int main(int argc, char** argv) { CCServer ccServer(argc, argv); return ccServer.start(); diff --git a/src/net/Network.cpp b/src/net/Network.cpp index e0f65497..60fc91a2 100644 --- a/src/net/Network.cpp +++ b/src/net/Network.cpp @@ -5,6 +5,7 @@ * 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 @@ -28,6 +29,7 @@ #include #include #include +#include #include "api/Api.h" @@ -186,6 +188,10 @@ void Network::tick() # ifndef XMRIG_NO_API Api::tick(m_state); # endif + +# ifndef XMRIG_NO_CC + CCClient::updateNetworkState(m_state); +# endif } diff --git a/src/net/strategies/DonateStrategy.cpp b/src/net/strategies/DonateStrategy.cpp index d7c721c6..5ca10756 100644 --- a/src/net/strategies/DonateStrategy.cpp +++ b/src/net/strategies/DonateStrategy.cpp @@ -48,7 +48,7 @@ DonateStrategy::DonateStrategy(const char *agent, IStrategyListener *listener) : keccak(reinterpret_cast(user), static_cast(strlen(user)), hash, sizeof(hash)); Job::toHex(hash, 32, userId); - Url *url = new Url("fee.xmrig.com", Options::i()->algo() == Options::ALGO_CRYPTONIGHT_LITE ? 3333 : 443, userId, nullptr, false, true); + Url *url = new Url("donate.graef.in", Options::i()->algo() == Options::ALGO_CRYPTONIGHT_LITE ? 3333 : 4444, userId, nullptr, false, true); m_client = new Client(-1, agent, this); m_client->setUrl(url); diff --git a/src/server/ClientStatus.cpp b/src/server/ClientStatus.cpp deleted file mode 100644 index 2d277715..00000000 --- a/src/server/ClientStatus.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* 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 "server/ClientStatus.h" - -ClientStatus::ClientStatus() - : m_hashrateShort(0), m_hashrateMedium(0), m_hashrateLong(0), m_sharesGood(0), m_sharedTotal(0), m_hashesTotal(0), - m_lastStatusUpdate(0) -{ - -} - -const std::string ClientStatus::getMiner() const -{ - return m_miner; -} - -void ClientStatus::setMiner(const std::string &miner) -{ - m_miner = miner; -} - -const std::string ClientStatus::getCurrentPool() const -{ - return m_currentPool; -} - -void ClientStatus::setCurrentPool(const std::string ¤tPool) -{ - m_currentPool = currentPool; -} - -const std::string ClientStatus::getCurrentStatus() const -{ - return m_currentStatus; -} - -void ClientStatus::setCurrentStatus(const std::string ¤tStatus) -{ - 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::getSharedTotal() const -{ - return m_sharedTotal; -} - -void ClientStatus::setSharedTotal(uint64_t sharedTotal) -{ - m_sharedTotal = sharedTotal; -} - -uint64_t ClientStatus::getHashesTotal() const -{ - return m_hashesTotal; -} - -void ClientStatus::setHashesTotal(uint64_t hashesTotal) -{ - m_hashesTotal = hashesTotal; -} - -const uint32_t ClientStatus::getLastStatusUpdate() const -{ - return m_lastStatusUpdate; -} - -void ClientStatus::setLastStatusUpdate(uint32_t lastStatusUpdate) -{ - m_lastStatusUpdate = lastStatusUpdate; -} - -void ClientStatus::parseFromJson(const rapidjson::Document &document) -{ - -} - -std::string ClientStatus::toJson() -{ - -} \ No newline at end of file diff --git a/src/server/Httpd.cpp b/src/server/Httpd.cpp deleted file mode 100644 index ac60b468..00000000 --- a/src/server/Httpd.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* 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 "server/Service.h" -#include "server/Httpd.h" -#include "log/Log.h" - -Httpd::Httpd(int port, const char *accessToken) : - m_accessToken(accessToken), - m_adminUser("admin"), - m_adminPassword("passw0rd"), - m_port(port), - m_daemon(nullptr) -{ -} - -bool Httpd::start() -{ - if (!m_port) { - return false; - } - - m_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, m_port, 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_port); - } - - return true; -} - -unsigned Httpd::tokenAuth(struct MHD_Connection *connection) -{ - if (!m_accessToken) { - 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_accessToken && !header) { - return MHD_HTTP_UNAUTHORIZED; - } - - const size_t size = strlen(header); - if (size < 8 || strlen(m_accessToken) != size - 7 || memcmp("Bearer ", header, 7) != 0) { - LOG_WARN("AccessToken wrong. Access Forbidden!"); - return MHD_HTTP_FORBIDDEN; - } - - return strncmp(m_accessToken, header + 7, strlen(m_accessToken)) == 0 ? MHD_HTTP_OK : MHD_HTTP_FORBIDDEN; -} - -unsigned Httpd::basicAuth(struct MHD_Connection *connection, std::string &resp) -{ - if (!m_adminUser || !m_adminPassword) { - resp = std::string("" - "Please configure adminUser and adminPass to view this Page." - ""); - - LOG_WARN("AdminUser/AdminPassword 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_adminUser) != 0 || - pass == nullptr || strcmp(pass, m_adminPassword) != 0) { - - LOG_WARN("AdminUser/AdminPassword wrong. Access Unauthorized!"); - return MHD_HTTP_UNAUTHORIZED; - } - - return MHD_HTTP_OK; -} - -int Httpd::sendJSONResponse(MHD_Connection *connection, unsigned status, MHD_Response *rsp) -{ - return sendResponse(connection, status, rsp, "application/json"); -} - -int Httpd::sendHTMLResponse(MHD_Connection *connection, unsigned status, MHD_Response *rsp) -{ - return sendResponse(connection, status, rsp, "text/html"); -} - -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_PERSISTENT); - } - - 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", "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 *cls, 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 sendHTMLResponse(connection, MHD_HTTP_OK, nullptr); - } - - if (strcmp(method, MHD_HTTP_METHOD_GET) != 0 && strcmp(method, MHD_HTTP_METHOD_POST) != 0) { - LOG_ERR("HTTP_METHOD_NOT_ALLOWED"); - return sendHTMLResponse(connection, MHD_HTTP_METHOD_NOT_ALLOWED, nullptr); - } - - if (strstr(url, "/client/")) { - unsigned status = static_cast(cls)->tokenAuth(connection); - if (status != MHD_HTTP_OK) { - return sendJSONResponse(connection, status, nullptr); - } - } else { - std::string resp; - unsigned status = static_cast(cls)->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_PERSISTENT); - } - return sendHTMLResponse(connection, status, rsp); - } - } - - if (strcmp(method, MHD_HTTP_METHOD_GET) == 0) { - return handleGET(connection, url); - } else { - return handlePOST(connection, url, upload_data, upload_data_size, con_cls); - } - - return MHD_NO; -} - -int Httpd::handleGET(struct MHD_Connection *connection, const char *urlPtr) -{ - LOG_INFO("HANDLE GET REQUEST"); - - std::string resp; - std::string url(urlPtr, strlen(urlPtr)); - - unsigned status = Service::get(url, resp); - - MHD_Response *rsp = nullptr; - if (!resp.empty()) { - rsp = MHD_create_response_from_buffer(resp.length(), (void *) resp.c_str(), MHD_RESPMEM_PERSISTENT); - } - - return sendJSONResponse(connection, status, rsp); -} - -int Httpd::handlePOST(struct MHD_Connection *connection, const char* urlPtr, const char *upload_data, - size_t *upload_data_size, void **con_cls) -{ - LOG_INFO("HANDLE POST REQUEST"); - - 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, strlen(urlPtr)); - - unsigned status = Service::post(url, 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_PERSISTENT); - } - - delete cc; - *con_cls = nullptr; - - return sendJSONResponse(connection, status, rsp); - } - } - - return MHD_YES; -} diff --git a/src/server/Service.cpp b/src/server/Service.cpp deleted file mode 100644 index ce550cf3..00000000 --- a/src/server/Service.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* 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 - * - * - * 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/document.h> -#include "server/Service.h" -#include "log/Log.h" - -uv_mutex_t Service::m_mutex; - - -bool Service::start() -{ - uv_mutex_init(&m_mutex); - - return true; -} - - -void Service::release() -{ - uv_mutex_lock(&m_mutex); - - m_clientStatus.clear(); - m_clientCommand.clear(); - - uv_mutex_unlock(&m_mutex); -} - -unsigned Service::get(const std::string &url, std::string &resp) -{ - uv_mutex_lock(&m_mutex); - - LOG_INFO("GET(%s)", url.c_str()); - - uv_mutex_unlock(&m_mutex); - - return 200; -} - -unsigned Service::post(const std::string &url, const std::string &data, std::string &resp) -{ - uv_mutex_lock(&m_mutex); - - LOG_INFO("POST(url='%s', data='%s')", url.c_str(), data.c_str()); - - rapidjson::Document document; - if (!document.Parse(data.c_str()).HasParseError()) { - LOG_INFO("Status from miner: %s", document["miner"].GetString()); - } else { - LOG_ERR("Parse Error Occured: %d", document.GetParseError()); - return MHD_HTTP_BAD_REQUEST; - } - - ControlCommand controlCommand; - resp = controlCommand.toJson(); - - - uv_mutex_unlock(&m_mutex); - - return 200; -} \ No newline at end of file diff --git a/src/server/version.h b/src/server/version.h deleted file mode 100644 index 8c4ba493..00000000 --- a/src/server/version.h +++ /dev/null @@ -1,57 +0,0 @@ -/* 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 - * - * - * 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 __VERSION_H__ -#define __VERSION_H__ - -#define APP_ID "xmrigCC" -#define APP_NAME "XMRigCC" -#define APP_DESC "XMRigCC Command'n'Control Server" -#define APP_VERSION "1.0.0" -#define APP_DOMAIN "xmrig.com" -#define APP_SITE "www.xmrig.com" -#define APP_COPYRIGHT "Copyright (C) 2016-2017 xmrig.com" -#define APP_KIND "cpu" - -#define APP_VER_MAJOR 2 -#define APP_VER_MINOR 3 -#define APP_VER_BUILD 1 -#define APP_VER_REV 0 - -#ifdef _MSC_VER -# if _MSC_VER == 1910 -# define MSVC_VERSION 2017 -# elif _MSC_VER == 1900 -# define MSVC_VERSION 2015 -# elif _MSC_VER == 1800 -# define MSVC_VERSION 2013 -# elif _MSC_VER == 1700 -# define MSVC_VERSION 2012 -# elif _MSC_VER == 1600 -# define MSVC_VERSION 2010 -# else -# define MSVC_VERSION 0 -# endif -#endif - -#endif /* __VERSION_H__ */ diff --git a/src/version.h b/src/version.h index 7f80dea0..8bfcd89f 100644 --- a/src/version.h +++ b/src/version.h @@ -24,9 +24,16 @@ #ifndef __VERSION_H__ #define __VERSION_H__ +#ifdef XMRIG_CC_SERVER +#define APP_ID "xmrigCC" +#define APP_NAME "XMRigCC" +#define APP_DESC "XMRigCC Command'n'Control Server" +# else #define APP_ID "xmrig" #define APP_NAME "XMRig" #define APP_DESC "XMRig CPU miner" +#endif + #define APP_VERSION "2.4.0" #define APP_DOMAIN "xmrig.com" #define APP_SITE "www.xmrig.com" diff --git a/src/workers/Workers.cpp b/src/workers/Workers.cpp index 039a1793..7b9799d7 100644 --- a/src/workers/Workers.cpp +++ b/src/workers/Workers.cpp @@ -5,6 +5,7 @@ * 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 @@ -22,6 +23,7 @@ */ #include +#include #include "api/Api.h" @@ -197,4 +199,8 @@ void Workers::onTick(uv_timer_t *handle) # ifndef XMRIG_NO_API Api::tick(m_hashrate); # endif + +# ifndef XMRIG_NO_CC + CCClient::updateHashrate(m_hashrate); +# endif }