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