Translate Udev Event path to a generic path

Given Udev event generated path is translated into sys bus path - as the json will
have the sys/bus path.

Test:
-> Translation successful for all frus including frus behind muxes.
-> If the system vpd is triggered by udev event, then the execution stops as
the system vpd will already be parsed via system-vpd.service.

Few Reasons why we translate the path to a generic path:
	-> Each i2c bus is memory mapped to a certain address in ASPEED.
	   In future if there are any architectural changes in the kernel, we need to
	   make changes to the JSON accordingly.
	-> Also in future if the system runs on a different ASPEED, we need to
	   update the JSON accordingly.
	-> This generic /sys/bus path represents the system wiring and doesnot rely on
	   how the busses are memory mapped.
	-> Also for call out based events, generic path is expected than the udev event path.

Sample Udev Event i2c path : "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a480.i2c-bus/i2c-8/8-0050/8-00500/nvmem"
Which gets translated to /sys/bus/ path as : "/sys/bus/i2c/drivers/at24/8-0050/eeprom"

Sample Udev Event spi path : "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/fsi-master/fsi0/slave@00:00/00:00:00:0a/fsi-master/fsi1/slave@03:00/01:03:00:04/spi_master/spi6/spi6.0/spi6.00/nvmem"
Which gets translated to /sys/bus/ path as : "/sys/bus/spi/drivers/at25/spi6.0/eeprom"

Sample Udev Event i2c path for frus behind mux : "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a300.i2c-bus/i2c-5/i2c-24/24-0051/24-00510/nvmem"
Which gets translated to generic path as /sys/bus/i2c/drivers/at24/24-0051/eeprom

Tested on simics:

<the inventory json should have generic eeprom paths for all frus>

root@rainier:/tmp# ./ibm-read-vpd-UG -f /sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/fsi-master/fsi0/slave@00:00/00:00:00:04/spi_master/spi12/spi12.0/spi12.00/nvmem
the path after translation : /sys/bus/spi/drivers/at25/spi12.0/eeprom

root@rainier:/tmp# ./ibm-read-vpd-UG -f /sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a480.i2c-bus/i2c-8/8-0051/8-00510/nvmem
the path after translation : /sys/bus/i2c/drivers/at24/8-0051/eeprom

root@rainier:/tmp# ./ibm-read-vpd-UG -f /sys/bus/i2c/drivers/at24/8-0051/8-00510/nvmem
the path after translation : /sys/bus/i2c/drivers/at24/8-0051/eeprom

Signed-off-by: PriyangaRamasamy <priyanga24@in.ibm.com>
Change-Id: I6d5995a85ef15e63d2d0b6f054fb0bf14a2b756c
diff --git a/const.hpp b/const.hpp
index 92107fa..e173e89 100644
--- a/const.hpp
+++ b/const.hpp
@@ -90,6 +90,9 @@
 constexpr auto motherBoardInterface =
     "xyz.openbmc_project.Inventory.Item.Board.Motherboard";
 constexpr auto systemVpdFilePath = "/sys/bus/i2c/drivers/at24/8-0050/eeprom";
+constexpr auto i2cPathPrefix = "/sys/bus/i2c/drivers/at24/";
+constexpr auto spiPathPrefix = "/sys/bus/spi/drivers/at25/";
+
 namespace lengths
 {
 enum Lengths
diff --git a/ibm_vpd_app.cpp b/ibm_vpd_app.cpp
index 6532982..1c212f8 100644
--- a/ibm_vpd_app.cpp
+++ b/ibm_vpd_app.cpp
@@ -887,7 +887,6 @@
 int main(int argc, char** argv)
 {
     int rc = 0;
-    string file{};
     json js{};
 
     // map to hold additional data in case of logging pel
@@ -902,9 +901,9 @@
 
     try
     {
+        string file{};
         App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
                 "in DBUS"};
-        string file{};
 
         app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
             ->required();
@@ -942,6 +941,22 @@
             throw(VpdJsonException("Json parsing failed", jsonToParse));
         }
 
+        // Check if it's a udev path - patterned as(/ahb/ahb:apb/ahb:apb:bus@)
+        if (file.find("/ahb:apb") != string::npos)
+        {
+            // Translate udev path to a generic /sys/bus/.. file path.
+            udevToGenericPath(file);
+            if (js["frus"][file].at(0).value("isSystemVpd", false))
+            {
+                return 0;
+            }
+        }
+
+        if (file.empty())
+        {
+            cerr << "The EEPROM path <" << file << "> is not valid.";
+            return 0;
+        }
         if ((js.find("frus") == js.end()) ||
             (js["frus"].find(file) == js["frus"].end()))
         {
@@ -970,13 +985,6 @@
 
         try
         {
-            // check if vpd file is empty
-            if (file.empty())
-            {
-                throw(VpdDataException(
-                    "VPD file is empty. Can't process with blank file."));
-            }
-
             Binary vpdVector = getVpdDataInVector(js, file);
             ParserInterface* parser = ParserFactory::getParser(move(vpdVector));
 
diff --git a/ibm_vpd_utils.cpp b/ibm_vpd_utils.cpp
index 15e3116..3c7304f 100644
--- a/ibm_vpd_utils.cpp
+++ b/ibm_vpd_utils.cpp
@@ -11,6 +11,7 @@
 #include <nlohmann/json.hpp>
 #include <phosphor-logging/elog-errors.hpp>
 #include <phosphor-logging/log.hpp>
+#include <regex>
 #include <sdbusplus/server.hpp>
 #include <sstream>
 #include <vector>
@@ -420,5 +421,71 @@
     return jsonPath;
 }
 
+void udevToGenericPath(string& file)
+{
+    // Sample udevEvent i2c path :
+    // "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a480.i2c-bus/i2c-8/8-0051/8-00510/nvmem"
+    // find if the path contains the word i2c in it.
+    if (file.find("i2c") != string::npos)
+    {
+        string i2cBusAddr{};
+
+        // Every udev i2c path should have the common pattern
+        // "i2c-bus_number/bus_number-vpd_address". Search for
+        // "bus_number-vpd_address".
+        regex i2cPattern("((i2c)-[0-9]+\\/)([0-9]+-[0-9]{4})");
+        smatch match;
+        if (regex_search(file, match, i2cPattern))
+        {
+            i2cBusAddr = match.str(3);
+        }
+        else
+        {
+            cerr << "The given udev path < " << file
+                 << " > doesn't match the required pattern. Skipping VPD "
+                    "collection."
+                 << endl;
+            exit(EXIT_SUCCESS);
+        }
+        // Forming the generic file path
+        file = i2cPathPrefix + i2cBusAddr + "/eeprom";
+    }
+    // Sample udevEvent spi path :
+    // "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/fsi-master/fsi0/slave@00:00/00:00:00:04/spi_master/spi2/spi2.0/spi2.00/nvmem"
+    // find if the path contains the word spi in it.
+    else if (file.find("spi") != string::npos)
+    {
+        // Every udev spi path will have common pattern "spi<Digit>/", which
+        // describes the spi bus number at which the fru is connected; Followed
+        // by a slash following the vpd address of the fru. Taking the above
+        // input as a common key, we try to search for the pattern "spi<Digit>/"
+        // using regular expression.
+        regex spiPattern("((spi)[0-9]+)(\\/)");
+        string spiBus{};
+        smatch match;
+        if (regex_search(file, match, spiPattern))
+        {
+            spiBus = match.str(1);
+        }
+        else
+        {
+            cerr << "The given udev path < " << file
+                 << " > doesn't match the required pattern. Skipping VPD "
+                    "collection."
+                 << endl;
+            exit(EXIT_SUCCESS);
+        }
+        // Forming the generic path
+        file = spiPathPrefix + spiBus + ".0/eeprom";
+    }
+    else
+    {
+        cerr << "\n The given EEPROM path < " << file
+             << " > is not valid. It's neither I2C nor "
+                "SPI path. Skipping VPD collection.."
+             << endl;
+        exit(EXIT_SUCCESS);
+    }
+}
 } // namespace vpd
 } // namespace openpower
\ No newline at end of file
diff --git a/ibm_vpd_utils.hpp b/ibm_vpd_utils.hpp
index 29996a9..4649adc 100644
--- a/ibm_vpd_utils.hpp
+++ b/ibm_vpd_utils.hpp
@@ -182,5 +182,9 @@
  */
 const string getIM(const Parsed& vpdMap);
 
+/** @brief Translate udev event generated path to a generic /sys/bus eeprom path
+ *  @param[io] file - path generated from udev event.
+ */
+void udevToGenericPath(string& file);
 } // namespace vpd
 } // namespace openpower
\ No newline at end of file