Collect Bad/Broken VPDs in BMC

When there is a vpd failure case due to the
corruption in vpd data, this commit collects the bad vpd
into BMC tmp/bad-vpd directory, so that this bad-vpd directory
gets into BMC Dump collection.

Tested on simics.
1.Corrupted the data of a vpd file.

 dd if=/dev/zero of=/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a500.i2c-bus/i2c-9/9-0050/9-00500/nvmem bs=1 seek=170 count=3

2.No bad-vpd directory before executing the commit changes.

root@rainier:/tmp# ls
dbus_properties.json                                                               systemd-private-9dfe8fba43254dfc8b7be9e4278a3ced-systemd-resolved.service-8pLWbo
ibm-read-vpd                                                                       systemd-private-9dfe8fba43254dfc8b7be9e4278a3ced-systemd-timesyncd.service-7yowK9
images                                                                             vpd-manager
overlays                                                                           vpd-tool
systemd-private-9dfe8fba43254dfc8b7be9e4278a3ced-dbus-broker.service-hb47BQ
root@rainier:/tmp#

3. Due to the data corruption, ibm-read-vpd throws exception

root@rainier:/tmp# ./ibm-read-vpd --file /sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a500.i2c-bus/i2c-9/9-0050/9-00500/nvmem
std::exception
root@rainier:/tmp#

4. And collects the broken vpd into bad-vpd directory in /tmp

root@rainier:/tmp# ls
bad-vpd                                                                            systemd-private-9dfe8fba43254dfc8b7be9e4278a3ced-dbus-broker.service-hb47BQ
dbus_properties.json                                                               systemd-private-9dfe8fba43254dfc8b7be9e4278a3ced-systemd-resolved.service-8pLWbo
ibm-read-vpd                                                                       systemd-private-9dfe8fba43254dfc8b7be9e4278a3ced-systemd-timesyncd.service-7yowK9
images                                                                             vpd-manager
overlays                                                                           vpd-tool
root@rainier:/tmp#

5. The bad vpd file is stored in /tmp/bad-vpd directory.

root@rainier:/tmp/bad-vpd# ls -l
-rw-r--r--    1 root     root         16384 Mar 28 19:54 i2c-9-0050
-rw-r--r--    1 root     root         65504 Mar 28 20:01 spi22

(vpd-names of i2c eeproms will be in the pattern "i2c-busNumber-eepromAddress";
for spi eeproms - "spiBusNumber")

Signed-off-by: PriyangaRamasamy <priyanga24@in.ibm.com>
Change-Id: I34fb8c61c79e02ca72d7e99413baebf7e5cb3d53
diff --git a/ibm_vpd_app.cpp b/ibm_vpd_app.cpp
index d7f32a8..ba121cc 100644
--- a/ibm_vpd_app.cpp
+++ b/ibm_vpd_app.cpp
@@ -887,7 +887,8 @@
 {
     int rc = 0;
     json js{};
-
+    Binary vpdVector{};
+    string file{};
     // map to hold additional data in case of logging pel
     PelAdditionalData additionalData{};
 
@@ -900,7 +901,6 @@
 
     try
     {
-        string file{};
         App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
                 "in DBUS"};
 
@@ -995,9 +995,8 @@
 
         try
         {
-            Binary vpdVector = getVpdDataInVector(js, file);
+            vpdVector = getVpdDataInVector(js, file);
             ParserInterface* parser = ParserFactory::getParser(vpdVector);
-
             variant<KeywordVpdMap, Store> parseResult;
             parseResult = parser->parse();
 
@@ -1034,7 +1033,7 @@
         additionalData.emplace("CALLOUT_INVENTORY_PATH",
                                INVENTORY_PATH + baseFruInventoryPath);
         createPEL(additionalData, pelSeverity, errIntfForEccCheckFail);
-
+        dumpBadVpd(file, vpdVector);
         cerr << ex.what() << "\n";
         rc = -1;
     }
@@ -1044,12 +1043,13 @@
         additionalData.emplace("CALLOUT_INVENTORY_PATH",
                                INVENTORY_PATH + baseFruInventoryPath);
         createPEL(additionalData, pelSeverity, errIntfForInvalidVPD);
-
+        dumpBadVpd(file, vpdVector);
         cerr << ex.what() << "\n";
         rc = -1;
     }
     catch (exception& e)
     {
+        dumpBadVpd(file, vpdVector);
         cerr << e.what() << "\n";
         rc = -1;
     }
diff --git a/ibm_vpd_utils.cpp b/ibm_vpd_utils.cpp
index 3c7304f..2413a3d 100644
--- a/ibm_vpd_utils.cpp
+++ b/ibm_vpd_utils.cpp
@@ -6,6 +6,7 @@
 #include "defines.hpp"
 #include "vpd_exceptions.hpp"
 
+#include <filesystem>
 #include <fstream>
 #include <iomanip>
 #include <nlohmann/json.hpp>
@@ -31,6 +32,7 @@
 using namespace openpower::vpd::exceptions;
 using namespace common::utility;
 using Severity = openpower::vpd::constants::PelSeverity;
+namespace fs = std::filesystem;
 
 // mapping of severity enum to severity interface
 static std::unordered_map<Severity, std::string> sevMap = {
@@ -487,5 +489,59 @@
         exit(EXIT_SUCCESS);
     }
 }
+string getBadVpdName(const string& file)
+{
+    string badVpd = BAD_VPD_DIR;
+    if (file.find("i2c") != string::npos)
+    {
+        badVpd += "i2c-";
+        regex i2cPattern("(at24/)([0-9]+-[0-9]+)\\/");
+        smatch match;
+        if (regex_search(file, match, i2cPattern))
+        {
+            badVpd += match.str(2);
+        }
+    }
+    else if (file.find("spi") != string::npos)
+    {
+        regex spiPattern("((spi)[0-9]+)(.0)");
+        smatch match;
+        if (regex_search(file, match, spiPattern))
+        {
+            badVpd += match.str(1);
+        }
+    }
+    return badVpd;
+}
+
+void dumpBadVpd(const string& file, const Binary& vpdVector)
+{
+    fs::path badVpdDir = BAD_VPD_DIR;
+    fs::create_directory(badVpdDir);
+    string badVpdPath = getBadVpdName(file);
+    if (fs::exists(badVpdPath))
+    {
+        std::error_code ec;
+        fs::remove(badVpdPath, ec);
+        if (ec) // error code
+        {
+            string error = "Error removing the existing broken vpd in ";
+            error += badVpdPath;
+            error += ". Error code : ";
+            error += ec.value();
+            error += ". Error message : ";
+            error += ec.message();
+            throw runtime_error(error);
+        }
+    }
+    ofstream badVpdFileStream(badVpdPath, ofstream::binary);
+    if (!badVpdFileStream)
+    {
+        throw runtime_error("Failed to open bad vpd file path in /tmp/bad-vpd. "
+                            "Unable to dump the broken/bad vpd file.");
+    }
+    badVpdFileStream.write(reinterpret_cast<const char*>(vpdVector.data()),
+                           vpdVector.size());
+}
 } // namespace vpd
 } // namespace openpower
\ No newline at end of file
diff --git a/ibm_vpd_utils.hpp b/ibm_vpd_utils.hpp
index 4649adc..8983985 100644
--- a/ibm_vpd_utils.hpp
+++ b/ibm_vpd_utils.hpp
@@ -26,7 +26,7 @@
 
 namespace inventory
 {
-/** @brief Api to obtain a dictionary of path -> services
+/** @brief API to obtain a dictionary of path -> services
  * where path is in subtree and services is of the type
  * returned by the GetObject method.
  *
@@ -186,5 +186,28 @@
  *  @param[io] file - path generated from udev event.
  */
 void udevToGenericPath(string& file);
+
+/**
+ * @brief API to generate a vpd name in some pattern.
+ * This vpd-name denotes name of the bad vpd file.
+ * For i2c eeproms - the pattern of the vpd-name will be
+ * i2c-<bus-number>-<eeprom-address>. For spi eeproms - the pattern of the
+ * vpd-name will be spi-<spi-number>.
+ *
+ * @param[in] file - file path of the vpd
+ * @return the vpd-name.
+ */
+string getBadVpdName(const string& file);
+
+/**
+ * @brief API which dumps the broken/bad vpd in a directory
+ * When the vpd is bad, this api places  the bad vpd file inside
+ * "/tmp/bad-vpd" in BMC, in order to collect bad VPD data as a part of user
+ * initiated BMC dump.
+ *
+ * @param[in] file - bad vpd file path
+ * @param[in] vpdVector - bad vpd vector
+ */
+void dumpBadVpd(const std::string& file, const Binary& vpdVector);
 } // namespace vpd
 } // namespace openpower
\ No newline at end of file
diff --git a/meson.build b/meson.build
index f9c340a..dac2a21 100644
--- a/meson.build
+++ b/meson.build
@@ -49,7 +49,8 @@
                        'INVENTORY_JSON_4U': '"'+get_option('INVENTORY_JSON_4U')+'"',
                        'INVENTORY_JSON_EVEREST': '"'+get_option('INVENTORY_JSON_EVEREST')+'"',
                        'DBUS_PROP_JSON': '"'+get_option('DBUS_PROP_JSON')+'"',
-                       'SYSTEM_JSON' : '"'+get_option('SYSTEM_JSON')+'"'
+                       'SYSTEM_JSON' : '"'+get_option('SYSTEM_JSON')+'"',
+                       'BAD_VPD_DIR': '"'+get_option('BAD_VPD_DIR')+'"'
                        }
   )
 
diff --git a/meson_options.txt b/meson_options.txt
index 20697d6..c95b48a 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -22,3 +22,4 @@
 option('INVENTORY_JSON_EVEREST',type: 'string', value: '/usr/share/vpd/50003000.json',  description: 'Inventory JSON for Everest system.')
 option('DBUS_PROP_JSON',type: 'string', value: '/usr/share/vpd/dbus_properties.json',  description: 'Json which contains properties specific to dbus.')
 option('SYSTEM_JSON',type: 'string', value: '/usr/share/vpd/systems.json',  description: 'JSON file used to pick the right system json')
+option('BAD_VPD_DIR',type: 'string', value: '/tmp/bad-vpd/', description: 'Directory which contains the bad vpd file - which needs to be included in bmc dump.')