Add Clear SEL command

Change-Id: I35b07e0bf1012d4b660d10cf17a47318f3c4e6e2
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/selutility.hpp b/selutility.hpp
index 50babca..2cae8c3 100644
--- a/selutility.hpp
+++ b/selutility.hpp
@@ -88,6 +88,23 @@
     uint16_t selRecordID;           //!< SEL Record ID.
 } __attribute__((packed));
 
+static constexpr auto initiateErase = 0xAA;
+static constexpr auto getEraseStatus = 0x00;
+static constexpr auto eraseComplete = 0x01;
+
+/** @struct ClearSELRequest
+ *
+ *  IPMI payload for Clear SEL command request.
+ */
+struct ClearSELRequest
+{
+    uint16_t reservationID;         //!< Reservation ID.
+    uint8_t charC;                  //!< Char 'C'(0x43h).
+    uint8_t charL;                  //!< Char 'L'(0x4Ch).
+    uint8_t charR;                  //!< Char 'R'(0x52h).
+    uint8_t eraseOperation;         //!< Erase operation.
+} __attribute__((packed));
+
 /** @brief Convert logging entry to SEL
  *
  *  @param[in] objPath - DBUS object path of the logging entry.
diff --git a/storagehandler.cpp b/storagehandler.cpp
index cd6f10c..5ded63e 100644
--- a/storagehandler.cpp
+++ b/storagehandler.cpp
@@ -296,6 +296,105 @@
     return IPMI_CC_OK;
 }
 
+ipmi_ret_t clearSEL(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
+                    ipmi_response_t response, ipmi_data_len_t data_len,
+                    ipmi_context_t context)
+{
+    auto requestData = reinterpret_cast<const ipmi::sel::ClearSELRequest*>
+            (request);
+
+    if (g_sel_reserve != requestData->reservationID)
+    {
+        *data_len = 0;
+        return IPMI_CC_INVALID_RESERVATION_ID;
+    }
+
+    if (requestData->charC != 'C' ||
+        requestData->charL != 'L' ||
+        requestData->charR != 'R')
+    {
+        *data_len = 0;
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    uint8_t eraseProgress = ipmi::sel::eraseComplete;
+
+    /*
+     * Erasure status cannot be fetched from DBUS, so always return erasure
+     * status as `erase completed`.
+     */
+    if (requestData->eraseOperation == ipmi::sel::getEraseStatus)
+    {
+        memcpy(response, &eraseProgress, sizeof(eraseProgress));
+        *data_len = sizeof(eraseProgress);
+        return IPMI_CC_OK;
+    }
+
+    sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
+    auto depth = 0;
+
+    auto mapperCall = bus.new_method_call(ipmi::sel::mapperBusName,
+                                          ipmi::sel::mapperObjPath,
+                                          ipmi::sel::mapperIntf,
+                                          "GetSubTreePaths");
+    mapperCall.append(ipmi::sel::logBasePath);
+    mapperCall.append(depth);
+    mapperCall.append(ipmi::sel::ObjectPaths({ipmi::sel::logEntryIntf}));
+
+    auto reply = bus.call(mapperCall);
+    if (reply.is_method_error())
+    {
+        memcpy(response, &eraseProgress, sizeof(eraseProgress));
+        *data_len = sizeof(eraseProgress);
+        return IPMI_CC_OK;
+    }
+
+    ipmi::sel::ObjectPaths objectPaths;
+    reply.read(objectPaths);
+    if (objectPaths.empty())
+    {
+        memcpy(response, &eraseProgress, sizeof(eraseProgress));
+        *data_len = sizeof(eraseProgress);
+        return IPMI_CC_OK;
+    }
+
+    std::string service;
+
+    try
+    {
+        service = ipmi::getService(bus,
+                                   ipmi::sel::logDeleteIntf,
+                                   objectPaths.front());
+    }
+    catch (const std::runtime_error& e)
+    {
+        log<level::ERR>(e.what());
+        *data_len = 0;
+        return IPMI_CC_UNSPECIFIED_ERROR;
+    }
+
+    for (const auto& iter : objectPaths)
+    {
+        auto methodCall = bus.new_method_call(service.c_str(),
+                                              iter.c_str(),
+                                              ipmi::sel::logDeleteIntf,
+                                              "Delete");
+
+        auto reply = bus.call(methodCall);
+        if (reply.is_method_error())
+        {
+            *data_len = 0;
+            return IPMI_CC_UNSPECIFIED_ERROR;
+        }
+    }
+
+    // Invalidate the cache of dbus entry objects.
+    cache::paths.clear();
+    memcpy(response, &eraseProgress, sizeof(eraseProgress));
+    *data_len = sizeof(eraseProgress);
+    return IPMI_CC_OK;
+}
+
 ipmi_ret_t ipmi_storage_get_sel_time(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
                               ipmi_request_t request, ipmi_response_t response,
                               ipmi_data_len_t data_len, ipmi_context_t context)
@@ -516,6 +615,11 @@
     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_STORAGE, IPMI_CMD_ADD_SEL);
     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_ADD_SEL, NULL, ipmi_storage_add_sel,
                            PRIVILEGE_OPERATOR);
+
+    // <Clear SEL>
+    printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_STORAGE, IPMI_CMD_CLEAR_SEL);
+    ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_CLEAR_SEL, NULL, clearSEL,
+                           PRIVILEGE_OPERATOR);
     return;
 }
 
diff --git a/storagehandler.h b/storagehandler.h
index e39d676..9973d14 100644
--- a/storagehandler.h
+++ b/storagehandler.h
@@ -10,6 +10,7 @@
     IPMI_CMD_GET_SEL_ENTRY  = 0x43,
     IPMI_CMD_ADD_SEL        = 0x44,
     IPMI_CMD_DELETE_SEL     = 0x46,
+    IPMI_CMD_CLEAR_SEL      = 0x47,
     IPMI_CMD_GET_SEL_TIME   = 0x48,
     IPMI_CMD_SET_SEL_TIME   = 0x49,