Session Commands Implementation

Implements Set Session Privilege Command and Close Session
command.

Change-Id: I18aeee7bcae48db3eb8a61292c9333ca2304dcf1
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/command/session_cmds.cpp b/command/session_cmds.cpp
new file mode 100644
index 0000000..f30ef78
--- /dev/null
+++ b/command/session_cmds.cpp
@@ -0,0 +1,79 @@
+#include "session_cmds.hpp"
+
+#include <iostream>
+
+#include "endian.hpp"
+#include "main.hpp"
+#include <host-ipmid/ipmid-api.h>
+
+namespace command
+{
+
+std::vector<uint8_t> setSessionPrivilegeLevel(std::vector<uint8_t>& inPayload,
+                                              const message::Handler& handler)
+{
+    std::cout << ">> setSessionPrivilegeLevel\n";
+
+    std::vector<uint8_t> outPayload(sizeof(SetSessionPrivLevelResp));
+    auto request = reinterpret_cast<SetSessionPrivLevelReq*>(inPayload.data());
+    auto response = reinterpret_cast<SetSessionPrivLevelResp*>
+                    (outPayload.data());
+    response->completionCode = IPMI_CC_OK;
+    uint8_t reqPrivilegeLevel = request->reqPrivLevel;
+
+    auto session = (std::get<session::Manager&>(singletonPool).getSession(
+                       handler.sessionID)).lock();
+
+    if (reqPrivilegeLevel == 0) // Just return present privilege level
+    {
+        response->newPrivLevel = static_cast<uint8_t>(session->curPrivLevel);
+    }
+    else if (reqPrivilegeLevel <= static_cast<uint8_t>(session->maxPrivLevel))
+    {
+        session->curPrivLevel = static_cast<session::Privilege>
+                                (reqPrivilegeLevel);
+        response->newPrivLevel = reqPrivilegeLevel;
+    }
+    else
+    {
+        // Requested level exceeds Channel and/or User Privilege Limit
+        response->completionCode = IPMI_CC_EXCEEDS_USER_PRIV;
+    }
+
+    std::cout << "<< setSessionPrivilegeLevel\n";
+    return outPayload;
+}
+
+std::vector<uint8_t> closeSession(std::vector<uint8_t>& inPayload,
+                                  const message::Handler& handler)
+{
+    std::cout << ">> closeSession\n";
+
+    std::vector<uint8_t> outPayload(sizeof(CloseSessionResponse));
+    auto request = reinterpret_cast<CloseSessionRequest*>(inPayload.data());
+    auto response = reinterpret_cast<CloseSessionResponse*>(outPayload.data());
+    response->completionCode = IPMI_CC_OK ;
+
+    auto bmcSessionID = endian::from_ipmi(request->sessionID);
+
+    // Session 0 is needed to handle session setup, so session zero is never
+    // closed
+    if (bmcSessionID == session::SESSION_ZERO)
+    {
+        response->completionCode = IPMI_CC_INVALID_SESSIONID;
+    }
+    else
+    {
+        auto status = std::get<session::Manager&>(singletonPool).stopSession
+                        (bmcSessionID);
+        if(!status)
+        {
+            response->completionCode = IPMI_CC_INVALID_SESSIONID;
+        }
+    }
+
+    std::cout << "<< closeSession\n";
+    return outPayload;
+}
+
+} // namespace command
diff --git a/command/session_cmds.hpp b/command/session_cmds.hpp
new file mode 100644
index 0000000..00dbfc4
--- /dev/null
+++ b/command/session_cmds.hpp
@@ -0,0 +1,118 @@
+#pragma once
+
+#include <vector>
+
+#include "message_handler.hpp"
+
+namespace command
+{
+
+constexpr uint8_t IPMI_CC_INVALID_PRIV_LEVEL = 0x80;
+constexpr uint8_t IPMI_CC_EXCEEDS_USER_PRIV = 0x81;
+
+/*
+ * @ struct SetSessionPrivLevelReq
+ *
+ * IPMI Request data for Set Session Privilege Level command
+ */
+struct SetSessionPrivLevelReq
+{
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+    uint8_t reqPrivLevel : 4;
+    uint8_t reserved : 4;
+#endif
+
+#if BYTE_ORDER == BIG_ENDIAN
+    uint8_t reserved : 4;
+    uint8_t reqPrivLevel : 4;
+#endif
+
+} __attribute__((packed));
+
+/*
+ * @ struct SetSessionPrivLevelResp
+ *
+ * IPMI Response data for Set Session Privilege Level command
+ */
+struct SetSessionPrivLevelResp
+{
+    uint8_t completionCode;
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+    uint8_t newPrivLevel : 4;
+    uint8_t reserved : 4;
+#endif
+
+#if BYTE_ORDER == BIG_ENDIAN
+    uint8_t reserved : 4;
+    uint8_t newPrivLevel : 4;
+#endif
+
+} __attribute__((packed));
+
+/*
+ * @brief Set Session Privilege Command
+ *
+ * This command is sent in authenticated format. When a session is activated,
+ * the session is set to an initial privilege level. A session that is
+ * activated at a maximum privilege level of Callback is set to an initial
+ * privilege level of Callback and cannot be changed. All other sessions are
+ * initially set to USER level, regardless of the maximum privilege level
+ * requested in the RAKP Message 1.
+ *
+ * This command cannot be used to set a privilege level higher than the lowest
+ * of the privilege level set for the user(via the Set User Access command) and
+ * the privilege limit for the channel that was set via the Set Channel Access
+ * command.
+ *
+ * @param[in] inPayload - Request Data for the command
+ * @param[in] handler - Reference to the Message Handler
+ *
+ * @return Response data for the command
+ */
+std::vector<uint8_t> setSessionPrivilegeLevel(std::vector<uint8_t>& inPayload,
+                                              const message::Handler& handler);
+
+constexpr uint8_t IPMI_CC_INVALID_SESSIONID = 0x87;
+
+/*
+ * @ struct CloseSessionRequest
+ *
+ * IPMI Request data for Close Session command
+ */
+struct CloseSessionRequest
+{
+    uint32_t sessionID;
+    uint8_t sessionHandle;
+} __attribute__((packed));
+
+/*
+ * @ struct CloseSessionResponse
+ *
+ * IPMI Response data for Close Session command
+ */
+struct CloseSessionResponse
+{
+    uint8_t completionCode;
+} __attribute__((packed));
+
+/*
+ * @brief Close Session Command
+ *
+ * This command is used to immediately terminate a session in progress. It is
+ * typically used to close the session that the user is communicating over,
+ * though it can be used to other terminate sessions in progress (provided that
+ * the user is operating at the appropriate privilege level, or the command is
+ * executed over a local channel - e.g. the system interface). Closing
+ * sessionless session ( session zero) is restricted in this command
+ *
+ * @param[in] inPayload - Request Data for the command
+ * @param[in] handler - Reference to the Message Handler
+ *
+ * @return Response data for the command
+ */
+std::vector<uint8_t> closeSession(std::vector<uint8_t>& inPayload,
+                                  const message::Handler& handler);
+
+} // namespace command
diff --git a/sessions_manager.cpp b/sessions_manager.cpp
index ff17727..a2d2d8d 100644
--- a/sessions_manager.cpp
+++ b/sessions_manager.cpp
@@ -97,16 +97,17 @@
     return getSession(sessionID);
 }
 
-void Manager::stopSession(SessionID bmcSessionID)
+bool Manager::stopSession(SessionID bmcSessionID)
 {
-    // If the session is valid and not session zero
-    if(bmcSessionID != SESSION_ZERO)
+    auto iter = sessionsMap.find(bmcSessionID);
+    if (iter != sessionsMap.end())
     {
-        auto iter = sessionsMap.find(bmcSessionID);
-        if (iter != sessionsMap.end())
-        {
-            iter->second->state = State::TEAR_DOWN_IN_PROGRESS;
-        }
+        iter->second->state = State::TEAR_DOWN_IN_PROGRESS;
+        return true;
+    }
+    else
+    {
+        return false;
     }
 }
 
diff --git a/sessions_manager.hpp b/sessions_manager.hpp
index 71bb7e4..8b9a76c 100644
--- a/sessions_manager.hpp
+++ b/sessions_manager.hpp
@@ -60,8 +60,10 @@
          *
          * @param[in] bmcSessionID - BMC Session ID
          *
+         * @return true on success and failure if session ID is invalid
+         *
          */
-        void stopSession(SessionID bmcSessionID);
+        bool stopSession(SessionID bmcSessionID);
 
         /*
          * @brief Get Session Handle