Fix IPMI SEL reservations and cancellations

Per the IPMI Spec, the SEL must be reserved to
   Delete an entry
   Clear the SEL
   Get a partial entry
   Add a partial entry

The current SEL reservation must be cancelled when
   A SEL entry is added
   A SEL entry is deleted
   The SEL is cleared
   The device is reset
   A new reservation is requested

This change adds a reservation status to track when a reservation
is active and a method to cancel the current reservation, and it
uses that to cancel the reservation in the Delete, Clear, and Add
SEL methods.

Change-Id: Ifd72e6d06ecc622855bd9ce8cc3928cbd0f2c34b
Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
diff --git a/host-ipmid/ipmid-api.h b/host-ipmid/ipmid-api.h
index 820776a..e77e720 100644
--- a/host-ipmid/ipmid-api.h
+++ b/host-ipmid/ipmid-api.h
@@ -1,5 +1,6 @@
 #ifndef __HOST_IPMID_IPMI_COMMON_H__
 #define __HOST_IPMID_IPMI_COMMON_H__
+#include <stdbool.h>
 #include <stdlib.h>
 #include <systemd/sd-bus.h>
 
@@ -77,7 +78,9 @@
 void ipmi_register_callback(ipmi_netfn_t, ipmi_cmd_t, ipmi_context_t,
                             ipmid_callback_t, ipmi_cmd_privilege_t);
 
-unsigned short get_sel_reserve_id(void);
+unsigned short reserveSel(void);
+bool checkSELReservation(unsigned short id);
+void cancelSELReservation(void);
 
 // These are the command network functions, the response
 // network functions are the function + 1. So to determine
diff --git a/ipmid.cpp b/ipmid.cpp
index bc6c522..be6b4d4 100644
--- a/ipmid.cpp
+++ b/ipmid.cpp
@@ -76,11 +76,29 @@
 std::map<ipmi_fn_cmd_t, ipmi_fn_context_t> g_ipmid_router_map;
 
 // IPMI Spec, shared Reservation ID.
-unsigned short g_sel_reserve = 0xFFFF;
+static unsigned short selReservationID = 0xFFFF;
+static bool selReservationValid = false;
 
-unsigned short get_sel_reserve_id(void)
+unsigned short reserveSel(void)
 {
-    return g_sel_reserve;
+    // IPMI spec, Reservation ID, the value simply increases against each
+    // execution of the Reserve SEL command.
+    if (++selReservationID == 0)
+    {
+        selReservationID = 1;
+    }
+    selReservationValid = true;
+    return selReservationID;
+}
+
+bool checkSELReservation(unsigned short id)
+{
+    return (selReservationValid && selReservationID == id);
+}
+
+void cancelSELReservation(void)
+{
+    selReservationValid = false;
 }
 
 namespace internal
diff --git a/storagehandler.cpp b/storagehandler.cpp
index 65497b2..0a46b83 100644
--- a/storagehandler.cpp
+++ b/storagehandler.cpp
@@ -38,7 +38,6 @@
 void register_netfn_storage_functions() __attribute__((constructor));
 
 unsigned int g_sel_time = 0xFFFFFFFF;
-extern unsigned short g_sel_reserve;
 extern const ipmi::sensor::IdInfoMap sensors;
 extern const FruMap frus;
 
@@ -161,7 +160,7 @@
 
     if (requestData->reservationID != 0)
     {
-        if (g_sel_reserve != requestData->reservationID)
+        if (!checkSELReservation(requestData->reservationID))
         {
             *data_len = 0;
             return IPMI_CC_INVALID_RESERVATION_ID;
@@ -275,12 +274,16 @@
     auto requestData =
         reinterpret_cast<const ipmi::sel::DeleteSELEntryRequest*>(request);
 
-    if (g_sel_reserve != requestData->reservationID)
+    if (!checkSELReservation(requestData->reservationID))
     {
         *data_len = 0;
         return IPMI_CC_INVALID_RESERVATION_ID;
     }
 
+    // Per the IPMI spec, need to cancel the reservation when a SEL entry is
+    // deleted
+    cancelSELReservation();
+
     try
     {
         ipmi::sel::readLoggingObjectPaths(cache::paths);
@@ -368,7 +371,7 @@
     auto requestData =
         reinterpret_cast<const ipmi::sel::ClearSELRequest*>(request);
 
-    if (g_sel_reserve != requestData->reservationID)
+    if (!checkSELReservation(requestData->reservationID))
     {
         *data_len = 0;
         return IPMI_CC_INVALID_RESERVATION_ID;
@@ -394,6 +397,9 @@
         return IPMI_CC_OK;
     }
 
+    // Per the IPMI spec, need to cancel any reservation when the SEL is cleared
+    cancelSELReservation();
+
     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
     ipmi::sel::ObjectPaths objectPaths;
     auto depth = 0;
@@ -581,16 +587,12 @@
                                     ipmi_context_t context)
 {
     ipmi_ret_t rc = IPMI_CC_OK;
+    unsigned short selResID = reserveSel();
 
-    // IPMI spec, Reservation ID, the value simply increases against each
-    // execution of reserve_sel command.
-    if (++g_sel_reserve == 0)
-        g_sel_reserve = 1;
-
-    *data_len = sizeof(g_sel_reserve);
+    *data_len = sizeof(selResID);
 
     // Pack the actual response
-    std::memcpy(response, &g_sel_reserve, *data_len);
+    std::memcpy(response, &selResID, *data_len);
 
     return rc;
 }
@@ -606,9 +608,13 @@
     ipmi_add_sel_request_t* p = (ipmi_add_sel_request_t*)request;
     uint16_t recordid;
 
+    // Per the IPMI spec, need to cancel the reservation when a SEL entry is
+    // added
+    cancelSELReservation();
+
     recordid = ((uint16_t)p->eventdata[1] << 8) | p->eventdata[2];
 
-    *data_len = sizeof(g_sel_reserve);
+    *data_len = sizeof(recordid);
 
     // Pack the actual response
     std::memcpy(response, &p->eventdata[1], 2);