Sessions Manager to manage IPMI sessions

IPMI Sessions manager is responsible for managing IPMI sessions and
routines for starting and stopping sessions.

Change-Id: Ic5c559fdc0dab0145e463689026c2f1c66f24a59
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/sessions_manager.cpp b/sessions_manager.cpp
new file mode 100644
index 0000000..ff17727
--- /dev/null
+++ b/sessions_manager.cpp
@@ -0,0 +1,166 @@
+#include "sessions_manager.hpp"
+
+#include <algorithm>
+#include <cstdlib>
+#include <iomanip>
+#include <iostream>
+#include <memory>
+
+#include "session.hpp"
+
+namespace session
+{
+
+Manager::Manager()
+{
+    /*
+     * Session ID is 0000_0000h for messages that are sent outside the session.
+     * The session setup commands are sent on this session, so when the session
+     * manager comes up, is creates the Session ID  0000_0000h. It is active
+     * through the lifetime of the Session Manager.
+     */
+    sessionsMap.emplace(0, std::make_shared<Session>());
+    // Seeding the pseudo-random generator
+    std::srand(std::time(0));
+}
+
+std::weak_ptr<Session> Manager::startSession(SessionID remoteConsoleSessID,
+        Privilege priv, cipher::rakp_auth::Algorithms authAlgo)
+{
+    std::shared_ptr<Session> session = nullptr;
+    SessionID sessionID = 0;
+    cleanStaleEntries();
+    auto activeSessions = sessionsMap.size() - MAX_SESSIONLESS_COUNT;
+
+    if (activeSessions < MAX_SESSION_COUNT)
+    {
+        do
+        {
+            session = std::make_shared<Session>(remoteConsoleSessID, priv);
+
+            /*
+             * Every IPMI Session has two ID's attached to it Remote Console
+             * Session ID and BMC Session ID. The remote console ID is passed
+             * along with the Open Session request command. The BMC session ID
+             * is the key for the session map and is generated using std::rand.
+             * There is a rare chance for collision of BMC session ID, so the
+             * following check validates that. In the case of collision the
+             * created session is reseted and a new session is created for
+             * validating collision.
+             */
+            auto iterator = sessionsMap.find(session->getBMCSessionID());
+            if (iterator != sessionsMap.end())
+            {
+               //Detected BMC Session ID collisions
+                session.reset();
+                continue;
+            }
+            else
+            {
+                break;
+            }
+        }
+        while (1);
+
+        // Set the Authentication Algorithm to RAKP_HMAC_SHA1
+        switch (authAlgo)
+        {
+            case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA1:
+            {
+                session->setAuthAlgo(
+                        std::make_unique<cipher::rakp_auth::AlgoSHA1>());
+                break;
+            }
+            default:
+            {
+                throw std::runtime_error("Invalid Authentication Algorithm");
+            }
+        }
+        sessionID = session->getBMCSessionID();
+        sessionsMap.emplace(sessionID, std::move(session));
+    }
+    else
+    {
+        std::cerr << "E> No free sessions left: Active: " << activeSessions <<
+                  " Allowed: " <<
+                  MAX_SESSION_COUNT << "\n";
+
+        for (const auto& iterator : sessionsMap)
+        {
+            std::cerr << "E> Active Session: 0x" << std::hex
+                      << std::setfill('0') << std::setw(8)
+                      << (iterator.second)->getBMCSessionID() << "\n";
+        }
+        throw std::runtime_error("No free sessions left");
+    }
+
+    return getSession(sessionID);
+}
+
+void 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())
+        {
+            iter->second->state = State::TEAR_DOWN_IN_PROGRESS;
+        }
+    }
+}
+
+std::weak_ptr<Session> Manager::getSession(SessionID sessionID,
+                                           RetrieveOption option)
+{
+    switch (option)
+    {
+        case RetrieveOption::BMC_SESSION_ID:
+        {
+            auto iter = sessionsMap.find(sessionID);
+            if (iter != sessionsMap.end())
+            {
+                return iter->second;
+            }
+        }
+        case RetrieveOption::RC_SESSION_ID:
+        {
+            auto iter = std::find_if(sessionsMap.begin(),
+                                     sessionsMap.end(),
+                                     [sessionID](const std::pair<const uint32_t,
+                                                 std::shared_ptr<Session>>& in)
+                                                 -> bool
+            {
+                return sessionID == in.second->getRCSessionID();
+            });
+
+            if (iter != sessionsMap.end())
+            {
+                return iter->second;
+            }
+        }
+        default:
+            throw std::runtime_error("Invalid retrieval option");
+    }
+
+    throw std::runtime_error("Session ID not found");
+}
+
+void Manager::cleanStaleEntries()
+{
+    for(auto iter = sessionsMap.begin(); iter != sessionsMap.end();)
+    {
+        auto session = iter->second;
+        if ((session->getBMCSessionID() != SESSION_ZERO) &&
+            !(session->isSessionActive()))
+        {
+            iter = sessionsMap.erase(iter);
+        }
+        else
+        {
+            ++iter;
+        }
+    }
+}
+
+} // namespace session