Initial read-daemon for hwmon entries
Discoveres hwmon entries for fans, input voltage, and temperature
sensors. Polls entries on a 1s interval and displays a message to
stdout if one of the entries have changed.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..49a7e4d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,60 @@
+LIBS += libphosphor-hwmon.so
+libphosphor-hwmon.so_OBJS += argument.o
+libphosphor-hwmon.so_OBJS += directory.o
+libphosphor-hwmon.so_OBJS += sensorset.o
+
+EXES += phosphor-hwmon-readd
+phosphor-hwmon-readd_OBJS += readd.o
+phosphor-hwmon-readd_LIBS += phosphor-hwmon
+
+#TODO: Issue#1 - Add the write-daemon for fan, pwm control.
+#EXES += phosphor-hwmon-writed
+#phosphor-hwmon-writed_OBJS += writed.o
+#phosphor-hwmon-writed_LIBS += phosphor-hwmon
+
+#### -----------------------------------------------------------------------####
+# #
+## Compilare Regulas Sequi ##
+# #
+#### -----------------------------------------------------------------------####
+
+CXXFLAGS ?= -O3 -g -pipe
+CXXFLAGS += --std=gnu++14 -Wall -flto -fPIC
+
+define __BUILD_EXE
+$1 : $$($1_OBJS) $$(LIBS)
+ $$(LINK.cpp) -o $$@ $$^
+
+#include $$($1_OBJS:.o=.d)
+endef
+
+$(foreach exe,$(EXES),$(eval $(call __BUILD_EXE,$(exe))))
+
+define __BUILD_LIB
+$1 : $$($1_OBJS)
+ $$(LINK.cpp) -shared -o $$@ $$^
+
+#include $$($1_OBJS:.o=.d)
+endef
+
+$(foreach lib,$(LIBS),$(eval $(call __BUILD_LIB,$(lib))))
+
+.PHONY: clean
+clean:
+ $(RM) $(foreach exe,$(EXES),$(exe) $($(exe)_OBJS)) \
+ $(foreach lib,$(LIBS),$(lib) $($(lib)_OBJS))
+
+DESTDIR ?= /
+BINDIR ?= /usr/bin
+LIBDIR ?= /usr/lib
+
+.PHONY: install
+install:
+ install -m 0755 -d $(DESTDIR)$(BINDIR)
+ install -m 0755 $(EXES) $(DESTDIR)$(BINDIR)
+ install -m 0755 -d $(DESTDIR)$(LIBDIR)
+ install -m 0755 $(LIBS) $(DESTDIR)$(LIBDIR)
+
+.DEFAULT_GOAL: all
+.PHONY: all
+all: $(EXES) $(LIBS)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..88122e6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+= phosphor-hwmon =
+
+Exposes generic hwmon entries as DBus objects.
diff --git a/argument.C b/argument.C
new file mode 100644
index 0000000..15c258e
--- /dev/null
+++ b/argument.C
@@ -0,0 +1,57 @@
+#include <iostream>
+#include <iterator>
+#include <algorithm>
+#include <cassert>
+#include "argument.H"
+
+ArgumentParser::ArgumentParser(int argc, char** argv)
+{
+ int option = 0;
+ while(-1 != (option = getopt_long(argc, argv, optionstr, options, NULL)))
+ {
+ if ((option == '?') || (option == 'h'))
+ {
+ usage(argv);
+ exit(-1);
+ }
+
+ auto i = &options[0];
+ while ((i->val != option) && (i->val != 0)) ++i;
+
+ if (i->val)
+ arguments[i->name] = (i->has_arg ? optarg : true_string);
+ }
+}
+
+const std::string& ArgumentParser::operator[](const std::string& opt)
+{
+ auto i = arguments.find(opt);
+ if (i == arguments.end())
+ {
+ return empty_string;
+ }
+ else
+ {
+ return i->second;
+ }
+}
+
+void ArgumentParser::usage(char** argv)
+{
+ std::cerr << "Usage: " << argv[0] << " [options]" << std::endl;
+ std::cerr << "Options:" << std::endl;
+ std::cerr << " --path=<path> sysfs location to monitor"
+ << std::endl;
+}
+
+const option ArgumentParser::options[] =
+{
+ { "path", required_argument, NULL, 'p' },
+ { "help", no_argument, NULL, 'h' },
+ { 0, 0, 0, 0},
+};
+
+const char* ArgumentParser::optionstr = "p:?h";
+
+const std::string ArgumentParser::true_string = "true";
+const std::string ArgumentParser::empty_string = "";
diff --git a/argument.H b/argument.H
new file mode 100644
index 0000000..252fc36
--- /dev/null
+++ b/argument.H
@@ -0,0 +1,28 @@
+#ifndef __ARGUMENT_H
+#define __ARGUMENT_H
+#include <getopt.h>
+#include <map>
+#include <string>
+
+class ArgumentParser
+{
+ public:
+ ArgumentParser(int argc, char** argv);
+ const std::string& operator[](const std::string& opt);
+
+ static void usage(char** argv);
+
+ static const std::string true_string;
+ static const std::string empty_string;
+
+ private:
+ std::map<const std::string, std::string> arguments;
+
+ static const option options[];
+ static const char* optionstr;
+
+ private:
+ ArgumentParser() {};
+};
+
+#endif
diff --git a/directory.C b/directory.C
new file mode 100644
index 0000000..5be9ff1
--- /dev/null
+++ b/directory.C
@@ -0,0 +1,38 @@
+#include <cerrno>
+#include <cstring>
+#include <iostream>
+#include "directory.H"
+
+Directory::Directory(const std::string& path) : entry(nullptr)
+{
+ dirp = opendir(path.c_str());
+ if (NULL == dirp)
+ {
+ auto e = errno;
+ std::cerr << "Error opening directory " << path.c_str()
+ << " : " << strerror(e) << std::endl;
+ }
+}
+
+Directory::~Directory()
+{
+ if (dirp)
+ {
+ closedir(dirp);
+ }
+}
+
+bool Directory::next(std::string& name)
+{
+ if (!dirp) return false;
+
+ dirent entry;
+ dirent* result;
+
+ auto rc = readdir_r(dirp, &entry, &result);
+
+ if ((rc) || (NULL == result)) return false;
+
+ name = entry.d_name;
+ return true;
+}
diff --git a/directory.H b/directory.H
new file mode 100644
index 0000000..1b085c5
--- /dev/null
+++ b/directory.H
@@ -0,0 +1,20 @@
+#ifndef __DIRECTORY_H
+#define __DIRECTORY_H
+
+#include <string>
+#include <dirent.h>
+
+class Directory
+{
+ public:
+ explicit Directory(const std::string& path);
+ ~Directory();
+
+ bool next(std::string& name);
+
+ private:
+ dirent* entry;
+ DIR* dirp;
+};
+
+#endif
diff --git a/hwmon.H b/hwmon.H
new file mode 100644
index 0000000..494d38e
--- /dev/null
+++ b/hwmon.H
@@ -0,0 +1,23 @@
+#ifndef __HWMON_H
+#define __HWMON_H
+
+#include <string>
+
+namespace hwmon
+{
+ using namespace std::literals;
+
+ namespace entry
+ {
+ static const std::string input = "input"s;
+ }
+
+ namespace type
+ {
+ static const std::string fan = "fan"s;
+ static const std::string temp = "temp"s;
+ static const std::string volt = "in"s;
+ }
+}
+
+#endif
diff --git a/readd.C b/readd.C
new file mode 100644
index 0000000..467ecbf
--- /dev/null
+++ b/readd.C
@@ -0,0 +1,79 @@
+#include <iostream>
+#include <memory>
+#include <thread>
+#include "argument.H"
+#include "sensorset.H"
+#include "sensorcache.H"
+#include "hwmon.H"
+#include "sysfs.H"
+
+static void exit_with_error(const char* err, char** argv)
+{
+ ArgumentParser::usage(argv);
+ std::cerr << std::endl;
+ std::cerr << "ERROR: " << err << std::endl;
+ exit(-1);
+}
+
+int main(int argc, char** argv)
+{
+ // Read arguments.
+ auto options = std::make_unique<ArgumentParser>(argc, argv);
+
+ // Parse out path argument.
+ auto path = (*options)["path"];
+ if (path == ArgumentParser::empty_string)
+ {
+ exit_with_error("Path not specified.", argv);
+ }
+
+ // Finished getting options out, so release the parser.
+ options.release();
+
+ // Check sysfs for available sensors.
+ auto sensors = std::make_unique<SensorSet>(path);
+ auto sensor_cache = std::make_unique<SensorCache>();
+
+ // TODO: Issue#3 - Need to make calls to the dbus sensor cache here to
+ // ensure the objects all exist?
+
+ // Polling loop.
+ while(true)
+ {
+ // Iterate through all the sensors.
+ for(auto& i : *sensors)
+ {
+ if (i.second.find(hwmon::entry::input) != i.second.end())
+ {
+ // Read value from sensor.
+ int value = 0;
+ read_sysfs(make_sysfs_path(path,
+ i.first.first, i.first.second,
+ hwmon::entry::input),
+ value);
+
+ // Update sensor cache.
+ if (sensor_cache->update(i.first, value))
+ {
+ // TODO: Issue#4 - dbus event here.
+ std::cout << i.first.first << i.first.second << " = "
+ << value << std::endl;
+ }
+ }
+ }
+
+ // Sleep until next interval.
+ // TODO: Issue#5 - Make this configurable.
+ // TODO: Issue#6 - Optionally look at polling interval sysfs entry.
+ {
+ using namespace std::literals::chrono_literals;
+ std::this_thread::sleep_for(1s);
+ }
+
+ // TODO: Issue#7 - Should probably periodically check the SensorSet
+ // for new entries.
+ }
+
+ return 0;
+}
+
diff --git a/sensorcache.H b/sensorcache.H
new file mode 100644
index 0000000..64333d8
--- /dev/null
+++ b/sensorcache.H
@@ -0,0 +1,27 @@
+#ifndef __SENSORCACHE_H
+#define __SENSORCACHE_H
+
+#include <map>
+
+class SensorCache
+{
+ public:
+ typedef std::map<std::pair<std::string, std::string>,
+ int> container_t;
+
+ bool update(const container_t::key_type& k,
+ const container_t::mapped_type& v)
+ {
+ auto& i = container[k];
+ if (v == i) return false;
+ else
+ {
+ i = v;
+ return true;
+ }
+ }
+ private:
+ container_t container;
+};
+
+#endif
diff --git a/sensorset.C b/sensorset.C
new file mode 100644
index 0000000..8b59ac1
--- /dev/null
+++ b/sensorset.C
@@ -0,0 +1,26 @@
+#include <regex>
+#include <iostream>
+#include "sensorset.H"
+#include "directory.H"
+
+// TODO: Issue#2 - STL regex generates really bloated code. Use POSIX regex
+// interfaces instead.
+static const std::regex sensors_regex =
+ std::regex("^(fan|in|temp)([0-9]+)_([a-z]*)", std::regex::extended);
+static const auto sensor_regex_match_count = 4;
+
+SensorSet::SensorSet(const std::string& path)
+{
+ Directory d(path);
+ std::string file;
+
+ while(d.next(file))
+ {
+ std::smatch match;
+ std::regex_search(file, match, sensors_regex);
+
+ if (match.size() != sensor_regex_match_count) continue;
+
+ container[make_pair(match[1],match[2])].emplace(match[3]);
+ }
+}
diff --git a/sensorset.H b/sensorset.H
new file mode 100644
index 0000000..beb8446
--- /dev/null
+++ b/sensorset.H
@@ -0,0 +1,31 @@
+#ifndef __SENSORSET_H
+#define __SENSORSET_H
+
+#include <map>
+#include <set>
+#include <string>
+
+class SensorSet
+{
+ public:
+ typedef std::map<std::pair<std::string, std::string>,
+ std::set<std::string>> container_t;
+
+ SensorSet(const std::string& path);
+
+ container_t::const_iterator begin()
+ {
+ return const_cast<const container_t&>(container).begin();
+ }
+
+ container_t::const_iterator end()
+ {
+ return const_cast<const container_t&>(container).end();
+ }
+
+ private:
+ container_t container;
+
+};
+
+#endif
diff --git a/sysfs.H b/sysfs.H
new file mode 100644
index 0000000..d073d77
--- /dev/null
+++ b/sysfs.H
@@ -0,0 +1,31 @@
+#ifndef __SYSFS_H
+#define __SYSFS_H
+
+#include <fstream>
+#include <string>
+
+template <typename T>
+void read_sysfs(const std::string& path, T& val)
+{
+ std::ifstream s(path);
+ s >> val;
+}
+
+template <typename T>
+void write_sysfs(const std::string& path, const T& val)
+{
+ std::ofstream s(path);
+ s << val;
+}
+
+const std::string make_sysfs_path(const std::string& path,
+ const std::string& type,
+ const std::string& id,
+ const std::string& entry)
+{
+ using namespace std::literals;
+
+ return path + "/"s + type + id + "_"s + entry;
+}
+
+#endif