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
diff --git a/sessions_manager.hpp b/sessions_manager.hpp
new file mode 100644
index 0000000..71bb7e4
--- /dev/null
+++ b/sessions_manager.hpp
@@ -0,0 +1,96 @@
+#pragma once
+
+#include <map>
+#include <memory>
+#include <mutex>
+
+#include "session.hpp"
+
+namespace session
+{
+
+enum class RetrieveOption
+{
+    BMC_SESSION_ID,
+    RC_SESSION_ID,
+};
+
+constexpr size_t SESSION_ZERO = 0;
+constexpr size_t MAX_SESSIONLESS_COUNT = 1;
+constexpr size_t MAX_SESSION_COUNT = 5;
+
+/*
+ * @class Manager
+ *
+ * Manager class acts a manager for the IPMI sessions and provides interfaces
+ * to start a session, stop a session and get reference to the session objects.
+ *
+ */
+
+class Manager
+{
+    public:
+
+        // BMC Session ID is the key for the map
+        using SessionMap = std::map<SessionID, std::shared_ptr<Session>>;
+
+        Manager();
+        ~Manager() = default;
+        Manager(const Manager&) = delete;
+        Manager& operator=(const Manager&) = delete;
+        Manager(Manager&&) = default;
+        Manager& operator=(Manager&&) = default;
+
+        /*
+         * @brief Start an IPMI session
+         *
+         * @param[in] remoteConsoleSessID - Remote Console Session ID mentioned
+         *            in the Open SessionRequest Command
+         * @param[in] priv - Privilege level requested
+         * @param[in] authAlgo - Authentication Algorithm
+         *
+         * @return session handle on success and nullptr on failure
+         *
+         */
+        std::weak_ptr<Session> startSession(SessionID remoteConsoleSessID,
+                Privilege priv, cipher::rakp_auth::Algorithms authAlgo);
+
+        /*
+         * @brief Stop IPMI Session
+         *
+         * @param[in] bmcSessionID - BMC Session ID
+         *
+         */
+        void stopSession(SessionID bmcSessionID);
+
+        /*
+         * @brief Get Session Handle
+         *
+         * @param[in] sessionID - Session ID
+         * @param[in] option - Select between BMC Session ID and Remote Console
+         *            Session ID, Default option is BMC Session ID
+         *
+         * @return session handle on success and nullptr on failure
+         *
+         */
+        std::weak_ptr<Session> getSession(
+               SessionID sessionID,
+               RetrieveOption option = RetrieveOption::BMC_SESSION_ID);
+
+    private:
+
+        /*
+         * @brief Session Manager keeps the session objects as a sorted
+         *        associative container with Session ID as the unique key
+         */
+        SessionMap sessionsMap;
+
+        /*
+         * @brief Clean Session Stale Entries
+         *
+         *  Removes the inactive sessions entries from the Session Map
+         */
+        void cleanStaleEntries();
+};
+
+} // namespace session