From f0c436051415023942aa85cd1926cb1665ffe2c4 Mon Sep 17 00:00:00 2001 From: Hayden Briese Date: Mon, 10 Apr 2017 02:07:51 +1000 Subject: [PATCH] Systemd journactl detection fix; log format minor change; improved first-use suggestions; refactoring; profiling fix; CMakeLists profiler option fix; ensure start PWM is sufficient; README changes --- CMakeLists.txt | 4 +- README.md | 53 +++++++++++++------------- TODO.md | 2 + src/Controller.cpp | 4 +- src/FanInterface.cpp | 12 +++++- src/Logging.cpp | 36 +++++++++++------- src/NvidiaUtil.cpp | 55 ++++++++++++++++----------- src/NvidiaUtil.hpp | 2 +- src/Util.cpp | 8 ++-- src/Util.hpp | 4 +- src/main.cpp | 89 +++++++++++++++++++++++++++++--------------- src/main.hpp | 4 +- 12 files changed, 168 insertions(+), 105 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f417339..ce4d963 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.3) -set(CMAKE_CXX_STANDARD 14) # TODO: C++17 support in CMake 3.8 (also cmake_minimum_required -> 3.8) +set(CMAKE_CXX_STANDARD 14) # TODO: C++17 when gcc 7 & clang 4 debain testing release; cmake_minimum_required -> 3.8 project(fancon) set(PACKAGE_AUTHOR "Hayden Briese ") @@ -81,7 +81,7 @@ endif () ## Google Perf Tools profiling option(PROFILE "Build debug release with support for CPU and heap profiling - REQUIRES Google Perf Tools" OFF) -if (PROFILE AND ${CMAKE_BUILD_TYPE} STREQUAL "Debug") +if (PROFILE AND CMAKE_BUILD_TYPE STREQUAL "Debug") find_package(Profiler REQUIRED) find_package(TCMalloc REQUIRED) # TCMalloc currently does not release memory set(LIBS ${LIBS} ${PROFILER_LIBRARY} ${TCMALLOC_LIBRARIES}) diff --git a/README.md b/README.md index fc975c2..936fe50 100644 --- a/README.md +++ b/README.md @@ -2,53 +2,54 @@ [![License](http://img.shields.io/badge/license-APACHE2-blue.svg)]() -fancon is a Linux fan control daemon and fan testing tool, allowing custom speed-temperature curves for fans, controllable by either PWM or RPM, or percentage. +A Linux fan control daemon and fan testing tool, allowing custom speed-temperature curves for fans, controllable by either PWM or RPM, or percentage. - High performance - Low memory usage - Support for system fans, and NVIDIA GPUs -Low overhead and easy configuration are the main goals of fancon, this is achieved by: - - Use of C++ and optimized STL functions +Low overhead and easy, meaningful configuration are the main goals of fancon, this is achieved by: - Extensive multi-threading support - - Standard text file configuration - at /etc/fancon.conf - - Fan characteristic testing - allowing more meaningful speed configuration such as through fan RPM, not just PWM control like similar tools - - Support for turing off fans (for example, if not under load) and correct handling of required fan PWM for re-starting + - Use of C++ and performance profiling + - Standard text file configuration - /etc/fancon.conf + - Fan characteristic testing - allowing more meaningful speed configuration such as through fan RPM or percentage, not just PWM like similar tools + - Speed percentage support (e.g. 65%) rather than cryptic fan-dependent PWM values (e.g. 142) + - Fans may be turned off (for example, when system is not under load) with a guaranteed start when they are required ### Installation -##### Install fancon snap: - -Snap [installation instructions](https://snapcraft.io/docs/core/install) +##### Build from source: +Tested with both gcc & clang -###### stable ```sh -$ sudo snap install fancon +$ sudo apt-get install gcc cmake libgcc-6-dev libc6-dev linux-libc-dev libc++-helpers lm-sensors libsensors4-dev libboost-system-dev libboost-filesystem-dev libboost-log-dev libpthread-stubs0-dev libpstreams-dev libsm-dev +$ sudo apt-get install libxnvctrl-dev libx11-dev +$ git clone https://github.com/HBriese/fancon.git && cd fancon +$ mkdir build; cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j && sudo make install ``` -###### git master -```sh -$ sudo snap install fancon --candidate -``` +| CMake Option | Default | Description | +|------------------|---------|---------------------------------------------------------------------------------------------| +| NVIDIA_SUPPORT | ON | Support for NVIDIA GPUs | +| STATIC_LIBSTDC++ | OFF | Statically link libstdc++ - useful for binary distribution | +| OPTIMIZE_DEBUG | OFF | Enable compiler optimizations on debug build | +| PROFILE | OFF | Support for Google Perf Tools CPU & heap profilers - for debug builds only, due to TCMalloc | +| LINT | OFF | Run lint checker (Clang-Tidy) | -##### Build from source: -gcc may be substituted for clang +##### Install from snap (currently not recommended): +Snap [installation instructions](https://snapcraft.io/docs/core/install) + +###### stable ```sh -$ sudo apt-get install gcc cmake libgcc-6-dev libc6-dev linux-libc-dev libc++-helpers lm-sensors libsensors4-dev libboost-system-dev libboost-filesystem-dev libboost-log-dev libpthread-stubs0-dev libpstreams-dev libsm-dev -$ sudo apt-get install libxnvctrl-dev libx11-dev -$ git clone https://github.com/HBriese/fancon.git && cd fancon -$ mkdir build; cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make && sudo make install +$ sudo snap install fancon ``` -CMake configure options: -- '-DNVIDIA_SUPPORT=OFF' (Default ON) disable support for NVIDIA GPUs - libxnvctrl-dev & libx11-dev are no longer required -- '-DSTATIC_LIBSTDC++=ON' Statically link libstdc++, useful for binary distribution to older systems/distributions ### Contributions Want to contribute? Pull requests, issues and feature requests are welcome. -Contributing developers please see TODO.md, or [email me](mailto:haydenbriese@gmail.com?subject=fancon). +Contributing developers please see TODO.md, or send an [email](mailto:haydenbriese@gmail.com?subject=fancon). The code is formatted to the [LLVM style](http://clang.llvm.org/docs/ClangFormatStyleOptions.html) using clang-format. @@ -58,7 +59,7 @@ The code is formatted to the [LLVM style](http://clang.llvm.org/docs/ClangFormat Copyright 2017 Hayden Briese Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. +you may not use fancon except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 diff --git a/TODO.md b/TODO.md index 4bb6a3f..5526157 100644 --- a/TODO.md +++ b/TODO.md @@ -2,6 +2,8 @@ ### Bugs +- Get xauth file & xdisplay from files in /etc/fancon.d/ + ### Features - Add support for AMD GPU fans diff --git a/src/Controller.cpp b/src/Controller.cpp index d375a3f..3241a6a 100644 --- a/src/Controller.cpp +++ b/src/Controller.cpp @@ -49,7 +49,7 @@ Controller::Controller(const string &configPath) { // Find sensors if it has already been defined auto sIt = find_if(sensors.begin(), sensors.end(), - [&sensorUID](const unique_ptr &s) { return *s == sensorUID; }); + [&](const unique_ptr &s) { return *s == sensorUID; }); // Add the sensor if it is missing, and update the sensor iterator if (sIt == sensors.end()) { @@ -103,7 +103,7 @@ ControllerState Controller::run() { threads.shrink_to_fit(); - LOG(llvl::debug) << "fancond started with " << threads.size() << " threads"; + LOG(llvl::debug) << "Started with " << threads.size() << " threads"; // Synchronize thread wake-up times startThreads(); diff --git a/src/FanInterface.cpp b/src/FanInterface.cpp index 4df4b72..c911687 100644 --- a/src/FanInterface.cpp +++ b/src/FanInterface.cpp @@ -157,7 +157,7 @@ void FanInterface::verifyPoints(const UID &fanUID) { /// \return True if <= max or >= min and non-zero auto withinBounds = - [&invalidPoints, &ignoring](const fan::Point &p, const auto &val, const auto &min, const auto &max) { + [&](const fan::Point &p, const auto &val, const auto &min, const auto &max) { if (val > max || ((val < min) & (val != 0))) { ignoring << ' ' << p; return !(invalidPoints = true); @@ -313,13 +313,21 @@ pwm_t FanInterface::getPWMStart(FanState &state, const milliseconds &waitTime) { // state = FanState::stopped; // assert(state == FanState::stopped); + // Find PWM at which fan starts pwm_t pwmStart = 0; while (readRPM() <= 0) { writePWM((pwmStart += 5)); sleep_for(waitTime); } - if (readPWM() != pwmStart) // pwm has changed since writing it to device + // Increase start PWM by an arbitrary amount to ensure start + const pwm_t arbInc = 10; + if ((pwmStart + arbInc) <= pwm_max_abs) + pwmStart += arbInc; + else + pwmStart = pwm_max_abs; + + if (readPWM() != pwmStart) // PWM has changed since writing it to device LOG(llvl::debug) << "PWM control is not exclusive - this may cause inaccurate testing results"; state = FanState::unknown; diff --git a/src/Logging.cpp b/src/Logging.cpp index d5967f8..b15a478 100644 --- a/src/Logging.cpp +++ b/src/Logging.cpp @@ -1,22 +1,29 @@ #include "Logging.hpp" +#include +#include + +using std::string; using namespace boost::log; namespace expr = boost::log::expressions; namespace { -bool systemdListening(int fileNumber) { +/// \return True if the file descriptor's device ID and inode number is being accessed by the systemd journal +bool systemdAccessing(int fileDescriptor) { + // Get systemd journal file access list const char *jstreamEnv = getenv("JOURNAL_STREAM"); if (!jstreamEnv) return false; + string js(jstreamEnv); - std::string jsEnv(jstreamEnv), stderrFN = std::to_string(fileNumber); - for (std::string::iterator it = jsEnv.begin(), endIt = jsEnv.end(), sep; - (sep = std::find(it, endIt, ':')) != endIt; it = std::next(sep)) { - if (std::string(it, sep) == stderrFN) - return true; - } + // Format of js: `device_id:inode_number` + struct stat s; + fstat(fileDescriptor, &s); + string fd = std::to_string(s.st_dev) + ':' + std::to_string(s.st_ino); - return false; + // Return true if fd is found in js + auto it = std::search(js.begin(), js.end(), fd.begin(), fd.end()); + return it != js.end(); } // TODO: replace with function returning a tuple @@ -24,7 +31,7 @@ template void setFormatter(boost::shared_ptr> sink, bool systemd) { if (!systemd) sink->set_formatter( - expr::format("%1% [%2%] <%3%> - %4%") + expr::format("%1% [%2%] <%3%> %4%") % expr::format_date_time(aux::default_attribute_names::timestamp(), "%m/%d %H:%M") % getpid() % expr::attr(aux::default_attribute_names::severity()) @@ -32,7 +39,7 @@ void setFormatter(boost::shared_ptr> sink ); else sink->set_formatter( - expr::format("<%1%> - %2%") + expr::format("<%1%> %2%") % expr::attr(aux::default_attribute_names::severity()) % expr::smessage ); @@ -43,18 +50,19 @@ BOOST_LOG_GLOBAL_LOGGER_INIT(logger, logger_t) { logger_t lg; core::get()->add_global_attribute(aux::default_attribute_names::timestamp(), attributes::local_clock()); - bool systemd = systemdListening(STDERR_FILENO); + bool systemd = systemdAccessing(STDERR_FILENO); - // Systemd-journal has it's own format attributes, so use a simpler formatter if outputting there - if (systemd || (isatty(STDERR_FILENO) > 0)) { + // Add cout & cerr sink if they are being by a TTY or systemd's journal + if (systemd || isatty(STDERR_FILENO)) { // llvl::info -> cout auto coutSink = add_console_log(std::cout, keywords::auto_flush = true, // Unspecified format is "%Message%" keywords::filter = (trivial::severity == llvl::info)); core::get()->add_sink(coutSink); - // non llvl::info -> cerr + // != llvl::info -> cerr auto cerrSink = add_console_log(std::cerr, keywords::auto_flush = true, keywords::filter = (trivial::severity != llvl::info)); + // Systemd-journal has it's own format attributes, so use a simpler formatter when outputting there setFormatter(cerrSink, systemd); core::get()->add_sink(cerrSink); } diff --git a/src/NvidiaUtil.cpp b/src/NvidiaUtil.cpp index 16812e8..6d7ae7c 100644 --- a/src/NvidiaUtil.cpp +++ b/src/NvidiaUtil.cpp @@ -55,9 +55,9 @@ bool NV::DisplayWrapper::open(string da, string xa) { if (!(dp = xlib.OpenDisplay(da.c_str()))) { stringstream err; if (forcedDa) // da.empty() == true // TODO: review check - guessed da may be correct - err << "Set \"display=\" in " << Util::conf_path << " to your display (echo $" << denv << ')'; + err << "Set \"display=\" in " << Util::config_path << " to your display (echo $" << denv << ')'; if (!getenv(xaenv)) - err << "Set \"xauthority=\" in " << Util::conf_path << " to your .Xauthority file (echo $" << xaenv << ')'; + err << "Set \"xauthority=\" in " << Util::config_path << " to your .Xauthority file (echo $" << xaenv << ')'; return false; } @@ -96,49 +96,62 @@ bool NV::supported() { } else if ((major < 1) || (major == 1 && minor < 9)) // XNVCTRL must be at least v1.9 LOG(llvl::warning) << "NV-CONTROL X version is not officially supported (too old!); please use v1.9 or higher"; + // Check coolbits value, actual change required restart - ONLY if run in a terminal, so user knows to restart + if (isatty(STDERR_FILENO) && enableFanControlCoolbit()) { + LOG(llvl::warning) << "RESTART system (or X11) to use NVIDIA fan control - fan control coolbits value enabled"; + return false; + } + return true; } -void NV::enableFanControlCoolbit() { +/// \return True if the system's coolbit value has been changed +bool NV::enableFanControlCoolbit() { // TODO: find and set Coolbits value without 'nvidia-xconfig' - for when not available (e.g. in snap confinement) - string command("sudo nvidia-xconfig -t | grep Coolbits"); - redi::pstream ips(command); + redi::pstream ips("sudo nvidia-xconfig -t | grep Coolbits"); string l; std::getline(ips, l); // Exit early with message if nvidia-xconfig isn't found if (l.empty()) { - LOG(llvl::info) << "nvidia-xconfig could not be found, either install it, or set coolbits value manually"; - return; + LOG(llvl::info) << "nvidia-xconfig could not be found, either install it, or set the coolbits value manually"; + return false; } - int iv = Util::lastNum(l); // Initial value - int cv(iv); // Current val + int initv = Util::lastNum(l), + curv = initv; const int nBits = 5; const int fcBit = 2; // 4 - int cbV[nBits] = {1, 2, 4, 8, 16}; // pow(2, bit) - int cbS[nBits]{0}; + int cbVal[nBits] = {1, 2, 4, 8, 16}; // bit^2 + bool cbSet[nBits]{0}; + // Determine set coolbit values for (auto i = nBits - 1; i >= 0; --i) - if ((cbS[i] = (cv / cbV[i]) >= 1)) - cv -= cbV[i]; + if ((cbSet[i] = (curv / cbVal[i]) >= 1)) + curv -= cbVal[i]; - int nv = iv; - if (cv > 0) { + // Value malformed if there are remaining 'bits' + auto newv = initv; + if (curv > 0) { LOG(llvl::error) << "Invalid coolbits value, fixing with fan control bit set"; - nv -= cv; + newv -= curv; } - if (!cbS[fcBit]) - nv += cbV[fcBit]; + // Increment coolbits value if manual fan control coolbit not set + if (!cbSet[fcBit]) + newv += cbVal[fcBit]; - if (nv != iv) { - command = string("sudo nvidia-xconfig --cool-bits=") + to_string(nv) + " > /dev/null"; + // Write new value if changes to the initial value have been made + bool ret; + if ((ret = newv != initv)) { + auto command = string("sudo nvidia-xconfig --cool-bits=") + to_string(newv) + " > /dev/null"; if (system(command.c_str()) != 0) - LOG(llvl::error) << "Failed to write coolbits value, nvidia fan test may fail!"; + LOG(llvl::error) << "Failed to write coolbits value, nvidia fan control may fail!"; LOG(llvl::info) << "Reboot, or restart your display server to enable NVIDIA fan control"; } + + return ret; } int NV::getNumGPUs() { diff --git a/src/NvidiaUtil.hpp b/src/NvidiaUtil.hpp index 4d353db..ed46e21 100644 --- a/src/NvidiaUtil.hpp +++ b/src/NvidiaUtil.hpp @@ -84,7 +84,7 @@ struct DisplayWrapper { extern const bool support; bool supported(); -void enableFanControlCoolbit(); // Doesn't work in snap confined +bool enableFanControlCoolbit(); // Doesn't work in snap confined int getNumGPUs(); vector nvProcessBinaryData(const unsigned char *data, const int len); diff --git a/src/Util.cpp b/src/Util.cpp index 62955d0..711eb13 100644 --- a/src/Util.cpp +++ b/src/Util.cpp @@ -22,7 +22,7 @@ int Util::lastNum(const string &str) { auto endIt = (endDigRevIt + 1).base(); bool numFound = false; - auto begDigRevIt = std::find_if(endDigRevIt, str.rend(), [&numFound](const char &c) { + auto begDigRevIt = std::find_if(endDigRevIt, str.rend(), [&](const char &c) { if (std::isdigit(c)) numFound = true; return numFound && !std::isdigit(c); @@ -42,10 +42,10 @@ bool Util::isNum(const string &str) { /// \return /// True if lock() has been called by a process that is currently running bool Util::locked() { - if (!exists(pid_file)) + if (!exists(pid_path)) return false; - auto pid = read < pid_t > (pid_file); + auto pid = read < pid_t > (pid_path); return exists(string("/proc/") + to_string(pid)); // && pid != getpid(); } @@ -55,7 +55,7 @@ void Util::lock() { LOG(llvl::error) << "A fancon process is already running, please close it to continue"; exit(EXIT_FAILURE); } else - write(pid_file, getpid()); + write(pid_path, getpid()); } /// \return True if lock is acquired diff --git a/src/Util.hpp b/src/Util.hpp index 696b288..cdf77de 100644 --- a/src/Util.hpp +++ b/src/Util.hpp @@ -41,8 +41,8 @@ fancon::DeviceType operator|(fancon::DeviceType lhs, fancon::DeviceType rhs); fancon::DeviceType operator&(fancon::DeviceType lhs, fancon::DeviceType rhs); namespace Util { -constexpr const char *pid_file = "/var/run/fancon.pid"; -constexpr const char *conf_path = "/etc/fancon.conf"; +constexpr const char *pid_path = "/var/run/fancon.pid"; +constexpr const char *config_path = "/etc/fancon.conf"; constexpr const char *fancon_dir = "/etc/fancon.d/"; constexpr const char *hwmon_path = "/sys/class/hwmon/hwmon"; constexpr const char *fancon_hwmon_path = "/etc/fancon.d/hwmon"; diff --git a/src/main.cpp b/src/main.cpp index 917e8f2..cf3c542 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,8 +9,8 @@ void f::help() { << "Available commands (and options ):\n" << "-lf list-fans Lists the UIDs of all fans\n" << "-ls list-sensors List the UIDs of all temperature sensors\n" - << "-wc write-config Appends missing fan UIDs to " << conf_path << '\n' - << "test Tests the fan characteristic of all fans, required for usage of RPM in " << conf_path + << "-wc write-config Appends missing fan UIDs to " << config_path << '\n' + << "test Tests the fan characteristic of all fans, required for usage of RPM in " << config_path << '\n' << " -r retries 4 Number of retries a test does before failure, increase if you think a failing fan can pass!\n" << "start Starts the fancon daemon\n" @@ -26,6 +26,44 @@ void f::help() { << "libx11-6 (libX11.so); libxnvctrl0 (libXNVCtrl.so)"; } +void f::suggestions(const char *fanconDir, const char *configPath) { + // Constants to modify console text font + const char *bold = "\033[1m", *red = "\033[31m", *resetFont = "\033[0m"; + + const bool suggestTest = !exists(fanconDir), + suggestConfig = !exists(configPath); + + if (suggestTest || suggestConfig) { + std::ostringstream messages; + int maxSize{}; + + if (suggestTest) { + constexpr const char *m = + "Run `sudo fancon test` to be able to configure fan profiles using RPM, or RPM percentage"; + constexpr const auto mSize = static_cast(strlength(m)); +// constexpr const auto mSize = static_cast(std::char_traits::length(m)); // TODO: C++17 + if (mSize > maxSize) + maxSize = mSize; + + messages << bold << red << m << '\n'; + } + + if (suggestConfig) { + const string m = + string("Edit ") + configPath + " to configure fan profiles, referencing `sudo fancon list-sensors`"; + const auto mSize = static_cast(m.size()); + if (mSize > maxSize) + maxSize = mSize; + + messages << resetFont << red << m << '\n'; + } + + LOG(llvl::info) << bold << setw(maxSize) << std::setfill('-') << left << '-' << '\n' + << resetFont << messages.str() << resetFont + << bold << setw(maxSize) << std::setfill('-') << left << '-' << '\n' << resetFont; + } +} + void f::listFans() { auto uids = Find::getFanUIDs(); stringstream ss; @@ -83,7 +121,7 @@ void f::listSensors() { } void f::testFans(uint testRetries, bool singleThreaded) { - appendConfig(conf_path); + appendConfig(config_path); auto fanUIDs = Find::getFanUIDs(); if (fanUIDs.empty()) { @@ -245,7 +283,7 @@ void f::start(const bool fork_) { exitParent(pid); // refresh pid file to reflect forks - write(Util::pid_file, getpid()); + write(Util::pid_path, getpid()); } // Set file mode @@ -255,9 +293,11 @@ void f::start(const bool fork_) { if (chdir("/") < 0) LOG(llvl::warning) << "Failed to set working directory"; - Controller controller(conf_path); - while (controller.run() == ControllerState::reload) - controller.reload(conf_path); + Controller controller(config_path); + while (controller.run() == ControllerState::reload) { + controller.reload(config_path); + LOG(llvl::info) << "Reloading..."; + } return; } @@ -265,7 +305,7 @@ void f::start(const bool fork_) { void f::sendSignal(ControllerState state) { // use kill() to send signal to the process if (Util::locked()) - kill(read(Util::pid_file), static_cast(state)); + kill(read(Util::pid_path), static_cast(state)); else if (state == ControllerState::reload) LOG(llvl::info) << "Cannot reload: fancond is not running"; } @@ -279,27 +319,14 @@ bool f::Option::setIfValid(const string &str) { int main(int argc, char *argv[]) { #ifdef FANCON_PROFILE - ProfilerStart("fancon_full"); - HeapProfilerStart("fancon_full"); + ProfilerStart("fancon_profiler"); + HeapProfilerStart("fancon_heap_profiler"); #endif //FANCON_PROFILE vector args; for (int i = 1; i < argc; ++i) args.push_back(string(argv[i])); - if (!exists(Util::fancon_dir) || !exists(conf_path)) { - create_directory(Util::fancon_dir); - const char *bold = "\033[1m", *red = "\033[31m", *resetFont = "\033[0m"; - const string message = - "First use: run 'sudo fancon test && sudo fancon -lf -ls', then configure fan profiles in /etc/fancon.conf"; - LOG(llvl::info) - << bold << setw(static_cast(message.size())) << std::setfill('-') << left << '-' << '\n' - << red << message << resetFont << '\n' - << bold << setw(static_cast(message.size())) << left << '-' << resetFont << "\n\n"; - - // TODO: Offer to run `test` - } - // Options and commands f::Option verbose("verbose", "v"), quiet("quiet", "q"), threads("threads", "t", true), fork("fork", "f"), retries("retries", "r", true); @@ -310,7 +337,7 @@ int main(int argc, char *argv[]) { f::Command help("help", "", &f::help, false, false, args.empty()), list_fans("list-fans", "lf", &f::listFans), list_sensors("list-sensors", "ls", &f::listSensors), - write_config("write-config", "wc", [] { f::appendConfig(conf_path); }), + write_config("write-config", "wc", [] { f::appendConfig(config_path); }), test("test", "", [&retries, &threads] { f::testFans((retries.called && retries.val > 0) ? retries.val : 4, (threads.called && threads.val == 1)); @@ -334,14 +361,14 @@ int main(int argc, char *argv[]) { std::transform(a->begin(), a->end(), a->begin(), ::tolower); // Search commands - auto cIt_tc = find_if(commands.begin(), commands.end(), [&a](auto &c) { return c.get() == *a; }); + auto cIt_tc = find_if(commands.begin(), commands.end(), [&](auto &c) { return c.get() == *a; }); if (cIt_tc != commands.end()) { cIt_tc->get().called = true; continue; } // Search options - auto oIt = find_if(options.begin(), options.end(), [&a](auto &o) { return o.get() == *a; }); + auto oIt = find_if(options.begin(), options.end(), [&](auto &o) { return o.get() == *a; }); if (oIt != options.end()) { auto &o = oIt->get(); @@ -377,6 +404,9 @@ int main(int argc, char *argv[]) { logLevel = llvl::error; fancon::log::setLevel(logLevel); + // Suggest usage + f::suggestions(Util::fancon_dir, config_path); + // Run called commands for (const auto &c : commands) { if (c.get().called) { @@ -394,11 +424,10 @@ int main(int argc, char *argv[]) { } } -#ifdef HEAP_PROFILE - ProfilerStop(); - HeapProfilerDump("End of main"); +#ifdef FANCON_PROFILE HeapProfilerStop(); -#endif //HEAP_PROFILE + ProfilerStop(); +#endif //FANCON_PROFILE return 0; } \ No newline at end of file diff --git a/src/main.hpp b/src/main.hpp index 91d35fd..08760cd 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -25,7 +25,7 @@ using fancon::Find; using fancon::Controller; using fancon::ControllerState; using fancon::FanTestResult; -using fancon::Util::conf_path; +using fancon::Util::config_path; namespace Util = fancon::Util; @@ -33,6 +33,8 @@ int main(int argc, char *argv[]); namespace fancon { void help(); +void suggestions(const char *fanconDir, const char *configPath); +constexpr size_t strlength(const char *s) { return (*s == 0) ? 0 : strlength(s + 1) + 1; } // TODO: C++17 - remove void listFans(); void listSensors();