Manage certificates created by applications

Added watch on certificate path to watch on certificates
created/updated by apps.

As part of watch notification, create new D-Bus new certificate
and for existing D-Bus object update the properties.

Tested:
Test case 1
1) Ensure no certificate is present
2) Restart certificate service
3) Restart bmcweb service
4) Verified that certificate object is created for the
   self-signed certificate created by bmcweb.

Test case 2
1) After a certificate is present
2) Modify the bmcweb certificate by replacing it
   with a valid certificate manually.
3) Verified that certificate manager is notified
and certificate objects properties are updated.

Test case 3
1) Upload CSR based certificate file
2) Verified that private key is appended to the file

Test case 4
1) Create a dummy file in certificate folder
2) Verified that notification is received and file is ignored

Test case 5
1) Verified install, replace, generate csr.

Change-Id: I7d1e3624958e4b68e5ba7bc6150c19b11fca501a
Signed-off-by: Marri Devender Rao <devenrao@in.ibm.com>
diff --git a/watch.cpp b/watch.cpp
new file mode 100644
index 0000000..c1ac789
--- /dev/null
+++ b/watch.cpp
@@ -0,0 +1,112 @@
+#include "watch.hpp"
+
+#include <sys/inotify.h>
+#include <unistd.h>
+
+#include <cstring>
+#include <filesystem>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+namespace phosphor
+{
+namespace certs
+{
+using namespace phosphor::logging;
+namespace fs = std::filesystem;
+using InternalFailure =
+    sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+
+Watch::Watch(sdeventplus::Event& event, std::string& certFile, Callback cb) :
+    event(event), callback(cb)
+{
+    // get parent directory of certificate file to watch
+    fs::path path = std::move(fs::path(certFile).parent_path());
+    try
+    {
+        if (!fs::exists(path))
+        {
+            fs::create_directories(path);
+        }
+    }
+    catch (fs::filesystem_error& e)
+    {
+        log<level::ERR>("Failed to create directory", entry("ERR=%s", e.what()),
+                        entry("DIRECTORY=%s", path.c_str()));
+        elog<InternalFailure>();
+    }
+    watchDir = path;
+    watchFile = fs::path(certFile).filename();
+    startWatch();
+}
+
+Watch::~Watch()
+{
+    stopWatch();
+}
+
+void Watch::startWatch()
+{
+    // stop any existing watch
+    stopWatch();
+
+    fd = inotify_init1(IN_NONBLOCK);
+    if (-1 == fd)
+    {
+        log<level::ERR>("inotify_init1 failed,",
+                        entry("ERR=%s", std::strerror(errno)));
+        elog<InternalFailure>();
+    }
+    wd = inotify_add_watch(fd, watchDir.c_str(), IN_CLOSE_WRITE);
+    if (-1 == wd)
+    {
+        close(fd);
+        log<level::ERR>("inotify_add_watch failed,",
+                        entry("ERR=%s", std::strerror(errno)),
+                        entry("WATCH=%s", watchDir.c_str()));
+        elog<InternalFailure>();
+    }
+
+    ioPtr = std::make_unique<sdeventplus::source::IO>(
+        event, fd, EPOLLIN,
+        [this](sdeventplus::source::IO&, int fd, uint32_t revents) {
+            const int size = sizeof(struct inotify_event) + NAME_MAX + 1;
+            std::array<char, size> buffer;
+            int length = read(fd, buffer.data(), buffer.size());
+            if (length >= static_cast<int>(sizeof(struct inotify_event)))
+            {
+                struct inotify_event* notifyEvent =
+                    reinterpret_cast<struct inotify_event*>(&buffer[0]);
+                if (notifyEvent->len)
+                {
+                    if (watchFile == notifyEvent->name)
+                    {
+                        callback();
+                    }
+                }
+            }
+            else
+            {
+                log<level::ERR>("Failed to read inotify event");
+            }
+        });
+}
+
+void Watch::stopWatch()
+{
+    if (-1 != fd)
+    {
+        if (-1 != wd)
+        {
+            inotify_rm_watch(fd, wd);
+        }
+        close(fd);
+    }
+    if (ioPtr)
+    {
+        ioPtr.reset();
+    }
+}
+
+} // namespace certs
+} // namespace phosphor