From 9385e21b9721d642a661d7f1299ef5cdf8adacb4 Mon Sep 17 00:00:00 2001 From: Tony Butler Date: Fri, 8 Oct 2021 21:00:51 -0600 Subject: [PATCH] Add pause-on-process feature --- CMakeLists.txt | 12 ++++++ cmake/flags.cmake | 9 ++++- src/base/base.cmake | 7 ++++ src/base/kernel/Platform.h | 11 ++++++ src/base/kernel/Platform_mac.cpp | 9 +++++ src/base/kernel/Platform_unix.cpp | 65 +++++++++++++++++++++++++++++++ src/base/kernel/Platform_win.cpp | 65 +++++++++++++++++++++++++++++++ src/core/Miner.cpp | 9 +++++ src/core/config/Config.cpp | 55 ++++++++++++++++++++++++++ src/core/config/Config.h | 11 ++++++ 10 files changed, 252 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dc36e64a..e81405d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ option(WITH_SSE4_1 "Enable SSE 4.1 for Blake2" ON) option(WITH_BENCHMARK "Enable builtin RandomX benchmark and stress test" ON) option(WITH_SECURE_JIT "Enable secure access to JIT memory" OFF) option(WITH_DMI "Enable DMI/SMBIOS reader" ON) +option(WITH_PAUSE_PROCESS "Enable Pause-on-Process feature" ON) option(BUILD_STATIC "Build static binary" OFF) option(ARM_TARGET "Force use specific ARM target 8 or 7" 0) @@ -147,6 +148,9 @@ if (XMRIG_OS_WIN) ) set(EXTRA_LIBS ws2_32 psapi iphlpapi userenv) + if (WITH_PAUSE_PROCESS) + list(APPEND EXTRA_LIBS shlwapi) + endif() elseif (XMRIG_OS_APPLE) list(APPEND SOURCES_OS src/App_unix.cpp @@ -171,6 +175,14 @@ else() ) set(EXTRA_LIBS pthread rt dl) + if ((WITH_PAUSE_PROCESS) AND + ((CMAKE_CXX_COMPILER_ID MATCHES Clang) OR + ((CMAKE_CXX_COMPILER_ID MATCHES GNU) AND + (${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS "9.0.0")) + ) + ) + list(APPEND EXTRA_LIBS stdc++fs) + endif() elseif (XMRIG_OS_FREEBSD) set(EXTRA_LIBS kvm pthread) endif() diff --git a/cmake/flags.cmake b/cmake/flags.cmake index ff5943a1..8a94e5fc 100644 --- a/cmake/flags.cmake +++ b/cmake/flags.cmake @@ -1,6 +1,9 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD 11) +if (WITH_PAUSE_PROCESS) + set(CMAKE_CXX_STANDARD 17) +endif() set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) @@ -53,7 +56,11 @@ if (CMAKE_CXX_COMPILER_ID MATCHES GNU) if (${CMAKE_VERSION} VERSION_LESS "3.1.0") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + if (WITH_PAUSE_PROCESS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + endif() endif() #set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -gdwarf-2") diff --git a/src/base/base.cmake b/src/base/base.cmake index 3246d6f9..4b5eb2e2 100644 --- a/src/base/base.cmake +++ b/src/base/base.cmake @@ -272,3 +272,10 @@ if (WITH_RANDOMX AND WITH_BENCHMARK) else() remove_definitions(/DXMRIG_FEATURE_BENCHMARK) endif() + + +if (WITH_PAUSE_PROCESS) + add_definitions(/DXMRIG_FEATURE_PAUSE_PROCESS) +else() + remove_definitions(/DXMRIG_FEATURE_PAUSE_PROCESS) +endif() diff --git a/src/base/kernel/Platform.h b/src/base/kernel/Platform.h index 04c212e6..a57de3dc 100644 --- a/src/base/kernel/Platform.h +++ b/src/base/kernel/Platform.h @@ -21,6 +21,10 @@ #include +#ifdef XMRIG_FEATURE_PAUSE_PROCESS +#include +#include +#endif #include "base/tools/String.h" @@ -47,10 +51,17 @@ public: static void setThreadPriority(int priority); static inline bool isUserActive(uint64_t ms) { return idleTime() < ms; } +#ifdef XMRIG_FEATURE_PAUSE_PROCESS + static inline bool isProcessActive(std::vector search) { return checkProcesses(search); } +#endif static inline const String &userAgent() { return m_userAgent; } static bool isOnBatteryPower(); static uint64_t idleTime(); +#ifdef XMRIG_FEATURE_PAUSE_PROCESS + static bool checkProcesses(std::vector&); + std::vector processList; +#endif private: static char *createUserAgent(); diff --git a/src/base/kernel/Platform_mac.cpp b/src/base/kernel/Platform_mac.cpp index d07e925d..4f1e4d18 100644 --- a/src/base/kernel/Platform_mac.cpp +++ b/src/base/kernel/Platform_mac.cpp @@ -109,6 +109,15 @@ bool xmrig::Platform::isOnBatteryPower() } +#ifdef XMRIG_FEATURE_PAUSE_PROCESS +bool xmrig::Platform::checkProcesses(std::vector& processList) +{ + // not yet implemented + return false; +} +#endif + + uint64_t xmrig::Platform::idleTime() { uint64_t idle_time = 0; diff --git a/src/base/kernel/Platform_unix.cpp b/src/base/kernel/Platform_unix.cpp index f5bbc193..8d228304 100644 --- a/src/base/kernel/Platform_unix.cpp +++ b/src/base/kernel/Platform_unix.cpp @@ -35,6 +35,12 @@ #include #include #include +#ifdef XMRIG_FEATURE_PAUSE_PROCESS +#include +#include +#include +#include +#endif #include "base/kernel/Platform.h" @@ -161,6 +167,65 @@ bool xmrig::Platform::isOnBatteryPower() } +#ifdef XMRIG_FEATURE_PAUSE_PROCESS +struct ci_char_traits : public std::char_traits { + static bool eq(char c1, char c2) { return std::tolower(c1) == std::tolower(c2); } + static bool ne(char c1, char c2) { return std::tolower(c1) != std::tolower(c2); } + static bool lt(char c1, char c2) { return std::tolower(c1) < std::tolower(c2); } + static int compare(const char* s1, const char* s2, size_t n) { + while( n-- != 0 ) { + if( std::tolower(*s1) < std::tolower(*s2) ) return -1; + if( std::tolower(*s1) > std::tolower(*s2) ) return 1; + ++s1; ++s2; + } + return 0; + } + static const char* find(const char* s, int n, char a) { + while( n-- > 0 && std::tolower(*s) != std::tolower(a) ) { + ++s; + } + return s; + } +}; + +bool IsNumeric(const std::string& s) +{ + return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit); +} + +bool xmrig::Platform::checkProcesses(std::vector& processList) +{ + const std::filesystem::path proc{"/proc/"}; + for(auto const& dirEnt: std::filesystem::directory_iterator{proc}) + { + if (dirEnt.is_directory() && IsNumeric(dirEnt.path().stem().string())) + { + std::string procFile = dirEnt.path().string() + "/cmdline"; + std::ifstream myfile (procFile); + if (myfile.is_open()) + { + std::string cmdLine; + std::getline(myfile, cmdLine); + myfile.close(); + std::basic_string_view cmdLineCI{ cmdLine.c_str() }; + if (!cmdLine.empty()) + { + for (auto const& processName : processList) + { + if (cmdLineCI.find(processName.c_str()) != std::string::npos) + { + return true; + } + } + } + } + } + } + return false; +} +#endif + + uint64_t xmrig::Platform::idleTime() { return std::numeric_limits::max(); diff --git a/src/base/kernel/Platform_win.cpp b/src/base/kernel/Platform_win.cpp index 76d81ae5..9f0b0acf 100644 --- a/src/base/kernel/Platform_win.cpp +++ b/src/base/kernel/Platform_win.cpp @@ -22,6 +22,14 @@ #include #include #include +#ifdef XMRIG_FEATURE_PAUSE_PROCESS +#include +#include +#include +#include +#include +#include +#endif #include "base/kernel/Platform.h" @@ -175,3 +183,60 @@ uint64_t xmrig::Platform::idleTime() return static_cast(GetTickCount() - info.dwTime); } + +#ifdef XMRIG_FEATURE_PAUSE_PROCESS +std::wstring s2ws(const std::string& s) +{ + int len; + int slength = (int)s.length() + 1; + len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); + std::wstring buf; + buf.resize(len); + MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, const_cast(buf.c_str()), len); + return buf; +} + +bool xmrig::Platform::checkProcesses(std::vector& processList) +{ + DWORD aProcesses[1024], cbNeeded; + unsigned int i; + DWORD dwProcessNameLen; + + if (EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) + { + for (i = 0; i < cbNeeded / sizeof(DWORD); i++) + { + if (aProcesses[i] != 0) + { + std::unique_ptr wszProcessName(new WCHAR[MAX_PATH]); + std::unique_ptr wszSearchName(new WCHAR[MAX_PATH]); + HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, aProcesses[i]); + if (NULL != hProcess) + { + HMODULE hMod; + DWORD cbNeeded; + if (EnumProcessModulesEx(hProcess, &hMod, sizeof(hMod), &cbNeeded, LIST_MODULES_ALL)) + { + dwProcessNameLen = MAX_PATH; + if (QueryFullProcessImageName(hProcess, 0, wszProcessName.get(), &dwProcessNameLen)) + { + for (auto const& searchName : processList) + { + std::wstring wrapper = s2ws(searchName); + wcsncpy_s(wszSearchName.get(), MAX_PATH / sizeof(WCHAR), wrapper.c_str(), wrapper.length()); + if (NULL != StrStrI(wszProcessName.get(), wszSearchName.get())) + { + CloseHandle(hProcess); + return true; + } + } + } + } + } + CloseHandle(hProcess); + } + } + } + return false; +} +#endif diff --git a/src/core/Miner.cpp b/src/core/Miner.cpp index 8ffdc63b..0b8c8b75 100644 --- a/src/core/Miner.cpp +++ b/src/core/Miner.cpp @@ -339,6 +339,9 @@ public: bool active = false; bool battery_power = false; bool user_active = false; +# ifdef XMRIG_FEATURE_PAUSE_PROCESS + bool process_active = false; +# endif bool enabled = true; int32_t auto_pause = 0; bool reset = true; @@ -640,6 +643,12 @@ void xmrig::Miner::onTimer(const Timer *) autoPause(d_ptr->user_active, Platform::isUserActive(config->idleTime()), YELLOW_BOLD("user active"), GREEN_BOLD("user inactive")); } +# ifdef XMRIG_FEATURE_PAUSE_PROCESS + if (config->isPauseOnProcess()) { + autoPause(d_ptr->process_active, Platform::isProcessActive(config->processList()), YELLOW_BOLD("process active"), GREEN_BOLD("process inactive")); + } +# endif + if (stopMiner) { stop(); } diff --git a/src/core/config/Config.cpp b/src/core/config/Config.cpp index 6150607d..5685d9f1 100644 --- a/src/core/config/Config.cpp +++ b/src/core/config/Config.cpp @@ -54,6 +54,9 @@ constexpr static uint32_t kIdleTime = 60U; const char *Config::kPauseOnBattery = "pause-on-battery"; const char *Config::kPauseOnActive = "pause-on-active"; +#ifdef XMRIG_FEATURE_PAUSE_PROCESS +const char *Config::kPauseOnProcess = "pause-on-process"; +#endif #ifdef XMRIG_FEATURE_OPENCL @@ -79,6 +82,9 @@ public: bool pauseOnBattery = false; CpuConfig cpu; uint32_t idleTime = 0; +# ifdef XMRIG_FEATURE_PAUSE_PROCESS + std::vector processList; +# endif # ifdef XMRIG_ALGO_RANDOMX RxConfig rx; @@ -109,6 +115,21 @@ public: idleTime = value.GetUint(); } } + +# ifdef XMRIG_FEATURE_PAUSE_PROCESS + void setPauseProcesses(const rapidjson::Value &value) + { + processList.clear(); + if (value.IsArray()) { + for (const rapidjson::Value &item : value.GetArray()) { + if (!item.IsString()) { + continue; + } + processList.push_back(item.GetString()); + } + } + } +# endif }; } // namespace xmrig @@ -144,6 +165,34 @@ uint32_t xmrig::Config::idleTime() const } +#ifdef XMRIG_FEATURE_PAUSE_PROCESS +std::vector xmrig::Config::processList() const +{ + return d_ptr->processList; +} + +rapidjson::Value xmrig::Config::getPauseProcesses(rapidjson::Document &doc) const +{ + rapidjson::Value out; + + if (!d_ptr->processList.empty()) { + auto &allocator = doc.GetAllocator(); + + out.SetArray(); + + for (const auto& process: d_ptr->processList) { + rapidjson::Value value; + value.SetString(process.c_str(), allocator); + out.PushBack(value, allocator); + } + } + else out.SetBool(false); + + return out; +} +#endif + + #ifdef XMRIG_FEATURE_OPENCL const xmrig::OclConfig &xmrig::Config::cl() const { @@ -214,6 +263,9 @@ bool xmrig::Config::read(const IJsonReader &reader, const char *fileName) d_ptr->pauseOnBattery = reader.getBool(kPauseOnBattery, d_ptr->pauseOnBattery); d_ptr->setIdleTime(reader.getValue(kPauseOnActive)); +# ifdef XMRIG_FEATURE_PAUSE_PROCESS + d_ptr->setPauseProcesses(reader.getArray(kPauseOnProcess)); +# endif d_ptr->cpu.read(reader.getValue(CpuConfig::kField)); @@ -305,4 +357,7 @@ void xmrig::Config::getJSON(rapidjson::Document &doc) const doc.AddMember(StringRef(kWatch), m_watch, allocator); doc.AddMember(StringRef(kPauseOnBattery), isPauseOnBattery(), allocator); doc.AddMember(StringRef(kPauseOnActive), (d_ptr->idleTime == 0U || d_ptr->idleTime == kIdleTime) ? Value(isPauseOnActive()) : Value(d_ptr->idleTime), allocator); +# ifdef XMRIG_FEATURE_PAUSE_PROCESS + doc.AddMember(StringRef(kPauseOnProcess), getPauseProcesses(doc), allocator); +# endif } diff --git a/src/core/config/Config.h b/src/core/config/Config.h index c2da8523..aebedece 100644 --- a/src/core/config/Config.h +++ b/src/core/config/Config.h @@ -21,6 +21,9 @@ #include +#ifdef XMRIG_FEATURE_PAUSE_PROCESS +#include +#endif #include "3rdparty/rapidjson/fwd.h" @@ -46,6 +49,9 @@ public: static const char *kPauseOnBattery; static const char *kPauseOnActive; +# ifdef XMRIG_FEATURE_PAUSE_PROCESS + static const char *kPauseOnProcess; +# endif # ifdef XMRIG_FEATURE_OPENCL static const char *kOcl; @@ -67,10 +73,15 @@ public: ~Config() override; inline bool isPauseOnActive() const { return idleTime() > 0; } +# ifdef XMRIG_FEATURE_PAUSE_PROCESS + inline bool isPauseOnProcess() const { return !processList().empty(); } +# endif bool isPauseOnBattery() const; const CpuConfig &cpu() const; uint32_t idleTime() const; + std::vector processList() const; + rapidjson::Value getPauseProcesses(rapidjson::Document &doc) const; # ifdef XMRIG_FEATURE_OPENCL const OclConfig &cl() const;