SessionHandle

exception.h

#ifndef EXCEPTION_H 
#define EXCEPTION_H
 

#include <boost/exception/all.hpp>
#include <string>
 

namespace utils
{

/*!
 * \brief error_key An unique ID given to the error according to its 
 *  location. Probably redundant, since we'll be using __FILE__ and 
 *  __LINE__, but it's a low-maintenance scheme, we'll keep it.
 */
typedef boost::error_info<struct tag_error_id, unsigned long> error_key;
/*!
 * \brief error_id Similar to boost::errinfo_errno, but without the 
 *  automatic error message mechanism. E.g., if we have an error 
 *  code 3 and use boost::errinfo_errno, it'll include the message 
 *  "No such process", which won't be suitable for our error,
 *  since it has a different meaning.
 */
typedef boost::error_info<struct tag_error_id, int> error_id;
typedef boost::error_info<struct tag_error_message, std::string>
    error_message;
/*!
 * \brief nested_exception Sometimes, we'll be using neste exceptions 
 *  just to implement a very basic stack trace. It' won't be a real 
 *  stack trace, since it has nothing outside of our own code, but 
 *  it's a starting point.
 */
typedef boost::error_info<struct tag_nested_exception, 
    boost::exception_ptr> nested_exception;
/*!
 * \brief The PCBBaseException struct. As the name states, 
 *  it's our base exception
 */
struct PCBBaseException : virtual std::exception, 
    virtual boost::exception { };
 

} // namespace utils
#endif // EXCEPTION_H


sshexception.h

#ifndef SSHEXCEPTION_H 
#define SSHEXCEPTION_H
 

#include "utils/exception.h"
 

#include <boost/exception/all.hpp>
#include <string>
 

namespace ssh
{
 

typedef boost::error_info<struct tag_ssh_error_id, int> ssh_error_id;
typedef boost::error_info<struct tag_ssh_error_string, std::string>
    ssh_error_string;
 

struct SSHBaseException : virtual utils::PCBBaseException { };
struct SSHConnectionError :
    virtual SSHBaseException { }; // SOCKET SONNECTION
struct SSHCreateSessionError : virtual SSHBaseException { };
struct SSHHandshakeError : virtual SSHBaseException { };
struct SSHAuthenticationError : virtual SSHBaseException { };
struct SSHDisconnectSessionError : 
    virtual SSHBaseException { }; // SESSION TERMINATION
struct SSHFreeSessionError : virtual SSHBaseException { };
struct SSHDisconnectionError : 
    virtual SSHBaseException { }; // SOCKET DISCONNECTION
 

} // namespace ssh
#endif // SSHEXCEPTION_H


misc.h

#ifndef MISC_H 
#define MISC_H
 

namespace utils
{
 

enum class CleanupState { NoCleanup, CleanupInProgress, CleanupDone };
 

} // namespace ssh
#endif // MISC_H


sessionhandle.h

#ifndef SESSIONHANDLE_H 
#define SESSIONHANDLE_H
 

#include "utils/asyncloop.h"
#include "utils/misc.h"
 

// EVEN THOUGH BOOST ASIO ISN'T USED HERE, 
// UNTIL I SOLVE THE PROBLEM WITH THE
// WIN32 #define, I CANNOT #include IT AFTER libssh2
// AVOID ERROR BECAUSE OF swprintf
#undef __STRICT_ANSI__
// #define FOR WIN7. CAN'T FIND sdkddkver.h ON MINGW
#define _WIN32_WINNT 0x0601

#include <boost/asio.hpp>
 

#include "libssh2_config.h"
#include <libssh2.h>
 

namespace ssh
{
 

class SessionHandle
{
public:
    SessionHandle();
    ~SessionHandle();
 

    void CloseSession();
    LIBSSH2_SESSION* GetSession() { return session; }
 

    // WE MAY ADD MOVE IN THE FUTURE, BUT NOT COPY
    SessionHandle(SessionHandle&&) = delete;
    SessionHandle& operator=(SessionHandle&&) = delete;
    SessionHandle(SessionHandle const&) = delete;
    SessionHandle& operator=(SessionHandle const&) = delete;
 

private:    
    utils::TaskState DoCloseSession();
 

    utils::CleanupState cleanupState;
    LIBSSH2_SESSION *session;
};
 

} // namespace ssh
#endif // SESSIONHANDLE_H


sessionhandle.cpp

#include "sshexception.h" 
#include "sessionhandle.h" 
using utils::AsyncLoopTimer;
 

#include <boost/bind.hpp>
#include <boost/bind/protect.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/lexical_cast.hpp>
using boost::asio::io_service;
using boost::asio::deadline_timer;
using boost::bind;
using boost::posix_time::milliseconds;
using boost::protect;
using boost::lexical_cast;
 

#include <string>
using std::string;
 

namespace ssh
{


SessionHandle::SessionHandle() : 
    cleanupState(utils::CleanupState::NoCleanup), 
    session(nullptr)
{
    session = libssh2_session_init();
 
    if (!session)
    {
        BOOST_THROW_EXCEPTION(SSHCreateSessionError() << 
            ssh_error_string("Error creating SSH session."));
    }
 
    libssh2_session_set_blocking(session, 0);
}
 
SessionHandle::~SessionHandle()
{
    // WE'LL ONLY TRY TO CLEANUP 
    // IF NO ATTEMPT HAS BEEN MADE YET
    if ((session != nullptr) && 
        (cleanupState == utils::CleanupState::NoCleanup))
    {
        try
        {
            CloseSession();
        }
        catch (...)
        { }
    }
}



void SessionHandle::CloseSession()
{
    io_service ios;
    deadline_timer dt(ios);
 
    dt.expires_from_now(milliseconds(10));
    dt.async_wait(bind(AsyncLoopTimer, 
        protect(bind(&SessionHandle::DoCloseSession, this)), 
        boost::ref(dt), 10));
 
    cleanupState = utils::CleanupState::CleanupInProgress;
    ios.run();
 
    session = nullptr;
    cleanupState = utils::CleanupState::CleanupDone;
}


utils::TaskState SessionHandle::DoCloseSession()
{
    int rc = libssh2_session_free(session);
 
    if (rc == LIBSSH2_ERROR_EAGAIN)
    {
        return utils::TASK_WORKING;
    }
 
    if (rc)
    {
        BOOST_THROW_EXCEPTION(SSHFreeSessionError() <<
            ssh_error_string("Error " + lexical_cast<string>(rc) + 
            " freeing SSH session"));
    }
 
    return utils::TASK_DONE;
}

} // namespace ssh



No comments:

Post a Comment