sync_manager: Add callback to do rsync

Call rsync when the subscribed file or directory is modified or
deleted. Don't create error logs as syncing is used for backup
and does not affect the system's operation. Any errors will be
logged into the journal.

Closes openbmc/openbmc#2918

Change-Id: I2671f0afd2924c15ea883d4d037c641c6e9680b4
Signed-off-by: Adriana Kobylak <anoo@us.ibm.com>
diff --git a/sync_manager.cpp b/sync_manager.cpp
index 6a50711..f83aebd 100644
--- a/sync_manager.cpp
+++ b/sync_manager.cpp
@@ -1,3 +1,9 @@
+#include <experimental/filesystem>
+#include <phosphor-logging/log.hpp>
+#include <sys/inotify.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include "config.h"
 #include "sync_manager.hpp"
 
 namespace phosphor
@@ -7,8 +13,66 @@
 namespace manager
 {
 
-int Sync::processEntry()
+using namespace phosphor::logging;
+namespace fs = std::experimental::filesystem;
+
+int Sync::processEntry(int mask, const fs::path& entryPath)
 {
+    int status{};
+    pid_t pid = fork();
+
+    if (pid == 0)
+    {
+        fs::path dst(ALT_RWFS / entryPath);
+
+        // rsync needs an additional --delete argument to handle file deletions
+        // so need to differentiate between the different file events.
+        if (mask & IN_CLOSE_WRITE)
+        {
+            if (!(fs::exists(dst)))
+            {
+                if (fs::is_directory(entryPath))
+                {
+                    // Source is a directory, create it at the destination.
+                    fs::create_directories(dst);
+                }
+                else
+                {
+                    // Source is a file, create the directory where this file
+                    // resides at the destination.
+                    fs::create_directories(dst.parent_path());
+                }
+            }
+
+            execl("/usr/bin/rsync", "rsync", "-a", entryPath.c_str(),
+                  dst.c_str(), nullptr);
+            // execl only returns on fail
+            log<level::ERR>("Error occurred during the rsync call",
+                            entry("ERRNO=%d", errno),
+                            entry("PATH=%s", entryPath.c_str()));
+            return -1;
+        }
+        else if (mask & IN_DELETE)
+        {
+            execl("/usr/bin/rsync", "rsync", "-a", "--delete",
+                  entryPath.c_str(), dst.c_str(), nullptr);
+            // execl only returns on fail
+            log<level::ERR>("Error occurred during the rsync delete call",
+                            entry("ERRNO=%d", errno),
+                            entry("PATH=%s", entryPath.c_str()));
+            return -1;
+        }
+    }
+    else if (pid > 0)
+    {
+        waitpid(pid, &status, 0);
+    }
+    else
+    {
+        log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", errno));
+        return -1;
+    }
+
     return 0;
 }