diff --git a/CMakeLists.txt b/CMakeLists.txt index 021ffc30..2afcf70a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ project(xmrig) option(WITH_LIBCPUID "Use Libcpuid" ON) option(WITH_AEON "CryptoNight-Lite support" ON) - +option(WITH_HTTPD "HTTP REST API" ON) include (CheckIncludeFile) @@ -191,6 +191,15 @@ if (HAVE_SYSLOG_H) set(SOURCES_SYSLOG src/log/SysLog.h src/log/SysLog.cpp) endif() +find_package(MHD REQUIRED) +if (WITH_HTTPD AND MHD_FOUND) + include_directories(${MHD_INCLUDE_DIRS}) + + set(HTTPD_SOURCES src/api/Httpd.h src/api/Httpd.cpp) +else() + add_definitions(/DXMRIG_NO_HTTPD) +endif() + include_directories(src) include_directories(src/3rdparty) include_directories(src/3rdparty/jansson) @@ -198,5 +207,5 @@ include_directories(${UV_INCLUDE_DIR}) add_subdirectory(src/3rdparty/jansson) -add_executable(xmrig ${HEADERS} ${SOURCES} ${SOURCES_OS} ${SOURCES_CPUID} ${HEADERS_CRYPTO} ${SOURCES_CRYPTO} ${SOURCES_SYSLOG}) -target_link_libraries(xmrig jansson ${UV_LIBRARIES} ${EXTRA_LIBS} ${CPUID_LIB}) +add_executable(xmrig ${HEADERS} ${SOURCES} ${SOURCES_OS} ${SOURCES_CPUID} ${HEADERS_CRYPTO} ${SOURCES_CRYPTO} ${SOURCES_SYSLOG} ${HTTPD_SOURCES}) +target_link_libraries(xmrig jansson ${UV_LIBRARIES} ${MHD_LIBRARY} ${EXTRA_LIBS} ${CPUID_LIB}) diff --git a/cmake/FindMHD.cmake b/cmake/FindMHD.cmake new file mode 100644 index 00000000..23e31c44 --- /dev/null +++ b/cmake/FindMHD.cmake @@ -0,0 +1,39 @@ +# - Try to find MHD +# Once done this will define +# +# MHD_FOUND - system has MHD +# MHD_INCLUDE_DIRS - the MHD include directory +# MHD_LIBRARY - Link these to use MHD + +find_path( + MHD_INCLUDE_DIR + NAMES microhttpd.h + DOC "microhttpd include dir" +) + +find_library( + MHD_LIBRARY + NAMES microhttpd microhttpd-10 libmicrohttpd libmicrohttpd-dll + DOC "microhttpd library" +) + +set(MHD_INCLUDE_DIRS ${MHD_INCLUDE_DIR}) +set(MHD_LIBRARIES ${MHD_LIBRARY}) + +# debug library on windows +# 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") + find_library( + MHD_LIBRARY_DEBUG + NAMES microhttpd_d microhttpd-10_d libmicrohttpd_d libmicrohttpd-dll_d + DOC "mhd debug library" + ) + set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG}) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(mhd DEFAULT_MSG MHD_INCLUDE_DIR MHD_LIBRARY) +mark_as_advanced(MHD_INCLUDE_DIR MHD_LIBRARY) + diff --git a/src/App.cpp b/src/App.cpp index c172c045..a944fe84 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -46,6 +46,10 @@ # include "log/SysLog.h" #endif +#ifndef XMRIG_NO_HTTPD +# include "api/Httpd.h" +#endif + App *App::m_self = nullptr; @@ -53,6 +57,7 @@ App *App::m_self = nullptr; App::App(int argc, char **argv) : m_console(nullptr), + m_httpd(nullptr), m_network(nullptr), m_options(nullptr) { @@ -92,6 +97,9 @@ App::App(int argc, char **argv) : App::~App() { + uv_tty_reset_mode(); + + delete m_httpd; delete m_console; } @@ -116,13 +124,17 @@ int App::exec() Mem::allocate(m_options->algo(), m_options->threads(), m_options->doubleHash(), m_options->hugePages()); Summary::print(); +# ifndef XMRIG_NO_HTTPD + m_httpd = new Httpd(m_options->apiPort(), m_options->apiToken()); + m_httpd->start(); +# endif + Workers::start(m_options->affinity(), m_options->priority()); m_network->connect(); const int r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); uv_loop_close(uv_default_loop()); - uv_tty_reset_mode(); delete m_network; diff --git a/src/App.h b/src/App.h index 77bf973b..781f78f2 100644 --- a/src/App.h +++ b/src/App.h @@ -32,6 +32,7 @@ class Console; +class Httpd; class Network; class Options; @@ -56,6 +57,7 @@ private: static App *m_self; Console *m_console; + Httpd *m_httpd; Network *m_network; Options *m_options; uv_signal_t m_signal; diff --git a/src/Options.cpp b/src/Options.cpp index 36d570b4..120f58bf 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -90,32 +90,35 @@ static char const short_options[] = "a:c:khBp:Px:r:R:s:t:T:o:u:O:v:Vl:S"; static struct option const options[] = { - { "algo", 1, nullptr, 'a' }, - { "av", 1, nullptr, 'v' }, - { "background", 0, nullptr, 'B' }, - { "config", 1, nullptr, 'c' }, - { "cpu-affinity", 1, nullptr, 1020 }, - { "cpu-priority", 1, nullptr, 1021 }, - { "donate-level", 1, nullptr, 1003 }, - { "help", 0, nullptr, 'h' }, - { "keepalive", 0, nullptr ,'k' }, - { "log-file", 1, nullptr, 'l' }, - { "max-cpu-usage", 1, nullptr, 1004 }, - { "nicehash", 0, nullptr, 1006 }, - { "no-color", 0, nullptr, 1002 }, - { "no-huge-pages", 0, nullptr, 1009 }, - { "pass", 1, nullptr, 'p' }, - { "print-time", 1, nullptr, 1007 }, - { "retries", 1, nullptr, 'r' }, - { "retry-pause", 1, nullptr, 'R' }, - { "safe", 0, nullptr, 1005 }, - { "syslog", 0, nullptr, 'S' }, - { "threads", 1, nullptr, 't' }, - { "url", 1, nullptr, 'o' }, - { "user", 1, nullptr, 'u' }, - { "user-agent", 1, nullptr, 1008 }, - { "userpass", 1, nullptr, 'O' }, - { "version", 0, nullptr, 'V' }, + { "algo", 1, nullptr, 'a' }, + { "av", 1, nullptr, 'v' }, + { "background", 0, nullptr, 'B' }, + { "config", 1, nullptr, 'c' }, + { "cpu-affinity", 1, nullptr, 1020 }, + { "cpu-priority", 1, nullptr, 1021 }, + { "donate-level", 1, nullptr, 1003 }, + { "help", 0, nullptr, 'h' }, + { "keepalive", 0, nullptr ,'k' }, + { "log-file", 1, nullptr, 'l' }, + { "max-cpu-usage", 1, nullptr, 1004 }, + { "nicehash", 0, nullptr, 1006 }, + { "no-color", 0, nullptr, 1002 }, + { "no-huge-pages", 0, nullptr, 1009 }, + { "pass", 1, nullptr, 'p' }, + { "print-time", 1, nullptr, 1007 }, + { "retries", 1, nullptr, 'r' }, + { "retry-pause", 1, nullptr, 'R' }, + { "safe", 0, nullptr, 1005 }, + { "syslog", 0, nullptr, 'S' }, + { "threads", 1, nullptr, 't' }, + { "url", 1, nullptr, 'o' }, + { "user", 1, nullptr, 'u' }, + { "user-agent", 1, nullptr, 1008 }, + { "userpass", 1, nullptr, 'O' }, + { "version", 0, nullptr, 'V' }, + { "api-port", 1, nullptr, 3000 }, + { "api-access-token", 1, nullptr, 3001 }, + { "api-worker-id", 1, nullptr, 3002 }, { 0, 0, 0, 0 } }; @@ -153,6 +156,14 @@ static struct option const pool_options[] = { }; +static struct option const api_options[] = { + { "port", 1, nullptr, 3000 }, + { "access-token", 1, nullptr, 3001 }, + { "worker-id", 1, nullptr, 3002 }, + { 0, 0, 0, 0 } +}; + + static const char *algo_names[] = { "cryptonight", # ifndef XMRIG_NO_AEON @@ -188,10 +199,13 @@ Options::Options(int argc, char **argv) : m_ready(false), m_safe(false), m_syslog(false), + m_apiToken(nullptr), + m_apiWorkerId(nullptr), m_logFile(nullptr), m_userAgent(nullptr), m_algo(0), m_algoVariant(0), + m_apiPort(0), m_donateLevel(kDonateLevel), m_maxCpuUsage(75), m_printTime(60), @@ -302,6 +316,16 @@ bool Options::parseArg(int key, const char *arg) m_colors = false; break; + case 3001: /* --access-token */ + free(m_apiToken); + m_apiToken = strdup(arg); + break; + + case 3002: /* --worker-id */ + free(m_apiWorkerId); + m_apiWorkerId = strdup(arg); + break; + case 'r': /* --retries */ case 'R': /* --retry-pause */ case 't': /* --threads */ @@ -310,6 +334,7 @@ bool Options::parseArg(int key, const char *arg) case 1004: /* --max-cpu-usage */ case 1007: /* --print-time */ case 1021: /* --cpu-priority */ + case 3000: /* --api-port */ return parseArg(key, strtol(arg, nullptr, 10)); case 'B': /* --background */ @@ -432,6 +457,12 @@ bool Options::parseArg(int key, uint64_t arg) } break; + case 3000: /* --api-port */ + if (arg <= 65536) { + m_apiPort = (int) arg; + } + break; + default: break; } @@ -548,6 +579,13 @@ void Options::parseConfig(const char *fileName) } } + json_t *api = json_object_get(config, "api"); + if (json_is_object(api)) { + for (size_t i = 0; i < ARRAY_SIZE(api_options); i++) { + parseJSON(&api_options[i], api); + } + } + json_decref(config); } diff --git a/src/Options.h b/src/Options.h index e85441d9..582f393f 100644 --- a/src/Options.h +++ b/src/Options.h @@ -54,11 +54,14 @@ public: static inline Options* i() { return m_self; } static Options *parse(int argc, char **argv); + inline apiPort() const { return m_apiPort; } 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; } @@ -107,10 +110,13 @@ private: bool m_ready; bool m_safe; bool m_syslog; + char *m_apiToken; + char *m_apiWorkerId; char *m_logFile; char *m_userAgent; int m_algo; int m_algoVariant; + int m_apiPort; int m_donateLevel; int m_maxCpuUsage; int m_printTime; diff --git a/src/api/Httpd.cpp b/src/api/Httpd.cpp new file mode 100644 index 00000000..9161d082 --- /dev/null +++ b/src/api/Httpd.cpp @@ -0,0 +1,69 @@ +/* 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 "api/Httpd.h" +#include "log/Log.h" + + +static const char kNotFound [] = "{\"error\":\"NOT_FOUND\"}"; +static const size_t kNotFoundSize = sizeof(kNotFound) - 1; + + +Httpd::Httpd(int port, const char *accessToken) : + m_accessToken(accessToken), + m_port(port), + m_daemon(nullptr) +{ +} + + +bool Httpd::start() +{ + if (!m_port) { + return false; + } + + m_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, 4455, nullptr, nullptr, &Httpd::handler, this, MHD_OPTION_END); + if (!m_daemon) { + LOG_ERR("HTTP Daemon failed to start."); + return false; + } + + return true; +} + + +int Httpd::handler(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) +{ + struct MHD_Response *rsp = MHD_create_response_from_buffer(kNotFoundSize, (void*)kNotFound, MHD_RESPMEM_PERSISTENT); + + MHD_add_response_header(rsp, "Content-Type", "application/json"); + + const int ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, rsp); + MHD_destroy_response(rsp); + return ret; +} diff --git a/src/api/Httpd.h b/src/api/Httpd.h new file mode 100644 index 00000000..32750beb --- /dev/null +++ b/src/api/Httpd.h @@ -0,0 +1,49 @@ +/* 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 __HTTPD_H__ +#define __HTTPD_H__ + + +#include + + +struct MHD_Daemon; +struct MHD_Connection; + + +class Httpd +{ +public: + Httpd(int port, const char *accessToken); + bool start(); + +private: + static int handler(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls); + + const char *m_accessToken; + const int m_port; + MHD_Daemon *m_daemon; +}; + +#endif /* __HTTPD_H__ */ diff --git a/src/config.json b/src/config.json index afc2936b..5a727e48 100644 --- a/src/config.json +++ b/src/config.json @@ -22,5 +22,10 @@ "keepalive": true, "nicehash": false } - ] + ], + "api": { + "port": 0, + "access-token": null, + "worker-id": null + } } \ No newline at end of file