sync_watch: Handle IN_IGNORED

When the hostname is updated via systemd, it modifies the /etc/hostname
file in a way that triggers a IN_CLOSE_NOWRITE and IN_IGNORED signal.

 * When IN_IGNORED is received, re-subscribe the file, since it means
   that the inotify watch was removed.

 * A subsequent IN_CLOSE_WRITE signal is received after the re-subscribe
   which takes care of syncing the hostname file.

Tested: The hostname was synced when changed multiple times in a row
using the REST API.

Change-Id: I30abfcdc8b33f77adc6349d345cc957b07898018
Signed-off-by: Adriana Kobylak <anoo@us.ibm.com>
diff --git a/sync_watch.cpp b/sync_watch.cpp
index c017716..88ccd89 100644
--- a/sync_watch.cpp
+++ b/sync_watch.cpp
@@ -19,9 +19,42 @@
 using namespace phosphor::logging;
 namespace fs = std::experimental::filesystem;
 
+void SyncWatch::addInotifyWatch(const fs::path& path)
+{
+    auto fd = inotify_init1(IN_NONBLOCK);
+    if (-1 == fd)
+    {
+        log<level::ERR>("inotify_init1 failed", entry("ERRNO=%d", errno),
+                        entry("FILENAME=%s", path.c_str()));
+        return;
+    }
+
+    auto wd = inotify_add_watch(fd, path.c_str(), IN_CLOSE_WRITE | IN_DELETE);
+    if (-1 == wd)
+    {
+        log<level::ERR>("inotify_add_watch failed", entry("ERRNO=%d", errno),
+                        entry("FILENAME=%s", path.c_str()));
+        close(fd);
+        return;
+    }
+
+    auto rc = sd_event_add_io(&loop, nullptr, fd, EPOLLIN, callback, this);
+    if (0 > rc)
+    {
+        log<level::ERR>("failed to add to event loop", entry("RC=%d", rc),
+                        entry("FILENAME=%s", path.c_str()));
+        inotify_rm_watch(fd, wd);
+        close(fd);
+        return;
+    }
+
+    fileMap[fd].insert(std::make_pair(wd, fs::path(path)));
+}
+
 SyncWatch::SyncWatch(sd_event& loop,
                      std::function<int(int, fs::path&)> syncCallback) :
-    syncCallback(syncCallback)
+    syncCallback(syncCallback),
+    loop(loop)
 {
     auto syncfile = fs::path(SYNC_LIST_DIR_PATH) / SYNC_LIST_FILE_NAME;
     if (fs::exists(syncfile))
@@ -30,42 +63,7 @@
         std::ifstream file(syncfile.c_str());
         while (std::getline(file, line))
         {
-            auto fd = inotify_init1(IN_NONBLOCK);
-            if (-1 == fd)
-            {
-                log<level::ERR>("inotify_init1 failed",
-                                entry("ERRNO=%d", errno),
-                                entry("FILENAME=%s", line.c_str()),
-                                entry("SYNCFILE=%s", syncfile.c_str()));
-                continue;
-            }
-
-            auto wd =
-                inotify_add_watch(fd, line.c_str(), IN_CLOSE_WRITE | IN_DELETE);
-            if (-1 == wd)
-            {
-                log<level::ERR>("inotify_add_watch failed",
-                                entry("ERRNO=%d", errno),
-                                entry("FILENAME=%s", line.c_str()),
-                                entry("SYNCFILE=%s", syncfile.c_str()));
-                close(fd);
-                continue;
-            }
-
-            auto rc =
-                sd_event_add_io(&loop, nullptr, fd, EPOLLIN, callback, this);
-            if (0 > rc)
-            {
-                log<level::ERR>("failed to add to event loop",
-                                entry("RC=%d", rc),
-                                entry("FILENAME=%s", line.c_str()),
-                                entry("SYNCFILE=%s", syncfile.c_str()));
-                inotify_rm_watch(fd, wd);
-                close(fd);
-                continue;
-            }
-
-            fileMap[fd].insert(std::make_pair(wd, fs::path(line)));
+            addInotifyWatch(line);
         }
     }
 }
@@ -109,6 +107,22 @@
         if (it1 != syncWatch->fileMap.end())
         {
             auto it2 = it1->second.begin();
+
+            // Watch was removed, re-add it if file still exists.
+            if (event->mask & IN_IGNORED)
+            {
+                if (fs::exists(it2->second))
+                {
+                    syncWatch->addInotifyWatch(it2->second);
+                }
+                else
+                {
+                    log<level::INFO>("The inotify watch was removed",
+                                     entry("FILENAME=%s", it2->second.c_str()));
+                }
+                return 0;
+            }
+
             auto rc = syncWatch->syncCallback(event->mask, it2->second);
             if (rc)
             {
diff --git a/sync_watch.hpp b/sync_watch.hpp
index 30ea5f7..3ad5afd 100644
--- a/sync_watch.hpp
+++ b/sync_watch.hpp
@@ -54,6 +54,12 @@
     static int callback(sd_event_source* s, int fd, uint32_t revents,
                         void* userdata);
 
+    /** @brief Adds an inotify watch to the specified file or directory path
+     *
+     *  @param[in] path - The path to the file or directory
+     */
+    void addInotifyWatch(const fs::path& path);
+
     /** @brief Map of file descriptors, watch descriptors, and file paths */
     using fd = int;
     using wd = int;
@@ -61,6 +67,9 @@
 
     /** @brief The callback function for processing the inotify event */
     std::function<int(int, fs::path&)> syncCallback;
+
+    /** @brief Persistent sd_event loop */
+    sd_event& loop;
 };
 
 } // namespace manager