Unverified Commit a2c204d4 authored by jean-christophe81's avatar jean-christophe81 Committed by GitHub
Browse files

enh(ssh): new multiplexer based on ASIO with new libssh2 version

REFS: MON-12695
parent a6eb86d1
# Global options.
cmake_minimum_required(VERSION 2.8.12)
cmake_minimum_required(VERSION 3.16)
project(connectors CXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
add_definitions("-D_GLIBCXX_USE_CXX11_ABI=1")
......@@ -11,17 +11,25 @@ set(CONNECTOR_VERSION "${CONNECTOR_MAJOR}.${CONNECTOR_MINOR}.${CONNECTOR_PATCH}"
add_definitions(-DCENTREON_CONNECTOR_VERSION=\"${CONNECTOR_VERSION}\")
include(${CMAKE_SOURCE_DIR}/cmake/Findclib.cmake)
include(${CMAKE_SOURCE_DIR}/cmake/FindSSH.cmake)
#include(${CMAKE_SOURCE_DIR}/cmake/FindSSH.cmake)
include(${CMAKE_SOURCE_DIR}/cmake/Findperl.cmake)
include(${CMAKE_BINARY_DIR}/conan_paths.cmake)
find_package(fmt REQUIRED)
find_package(spdlog REQUIRED)
find_package(asio REQUIRED)
find_package(Boost 1.78.0 REQUIRED)
find_package(Libssh2 REQUIRED)
add_definitions(${spdlog_DEFINITIONS})
include_directories(${fmt_INCLUDE_DIRS})
include_directories(${spdlog_INCLUDE_DIRS})
include_directories(${asio_INCLUDE_DIRS})
include_directories(${Boost_INCLUDE_DIRS})
# Generate code to dynamically load modules.
add_custom_command(OUTPUT "${CMAKE_SOURCE_DIR}/perl/src/xs_init.cc"
......@@ -55,6 +63,7 @@ if (WITH_TESTING)
include_directories(${CMAKE_SOURCE_DIR}/perl/inc)
include_directories(${CMAKE_SOURCE_DIR}/ssh/inc)
include_directories(${Libssh2_INCLUDE_DIR})
add_definitions(-DBUILD_PATH="${CMAKE_BINARY_DIR}")
add_executable(ut
......@@ -66,13 +75,10 @@ if (WITH_TESTING)
${CMAKE_SOURCE_DIR}/perl/src/xs_init.cc
${CMAKE_SOURCE_DIR}/ssh/src/checks/check.cc
${CMAKE_SOURCE_DIR}/ssh/src/checks/result.cc
${CMAKE_SOURCE_DIR}/ssh/src/checks/timeout.cc
${CMAKE_SOURCE_DIR}/ssh/src/multiplexer.cc
${CMAKE_SOURCE_DIR}/ssh/src/orders/options.cc
${CMAKE_SOURCE_DIR}/ssh/src/orders/parser.cc
${CMAKE_SOURCE_DIR}/ssh/src/sessions/credentials.cc
${CMAKE_SOURCE_DIR}/ssh/src/sessions/session.cc
${CMAKE_SOURCE_DIR}/ssh/src/sessions/socket_handle.cc
${CMAKE_SOURCE_DIR}/ssh/src/reporter.cc
# Test sources.
${CMAKE_SOURCE_DIR}/perl/test/main.cc
......@@ -87,5 +93,9 @@ if (WITH_TESTING)
${CMAKE_SOURCE_DIR}/ssh/test/sessions.cc
)
target_link_libraries(ut ${GTest_LIBS} ${CLIB_LIBRARIES} ${PERL_LIBRARIES} ${fmt_LIBS} ${spdlog_LIBS} ${LIBSSH2_LIBRARIES})
target_precompile_headers(ut PRIVATE ${CMAKE_SOURCE_DIR}/ssh/precomp_inc/precomp.hh)
target_link_libraries(ut ${GTest_LIBS} ${CLIB_LIBRARIES} ${PERL_LIBRARIES} ${fmt_LIBS} ${spdlog_LIBS} ${Libssh2_LIBRARIES})
file(COPY ${CMAKE_SOURCE_DIR}/test/test_pattern DESTINATION ${CMAKE_BINARY_DIR})
endif (WITH_TESTING)
......@@ -91,7 +91,6 @@ if [ -r /etc/centos-release ] ; then
pkgs=(
perl-Thread-Queue
perl-devel
libssh2-devel
libgcrypt-devel
)
for i in "${pkgs[@]}"; do
......@@ -140,7 +139,6 @@ elif [ -r /etc/issue ] ; then
python3
python3-pip
libperl-dev
libssh2-1-dev
libgcrypt20-dev
)
for i in "${pkgs[@]}"; do
......@@ -158,7 +156,6 @@ elif [ -r /etc/issue ] ; then
python3
python3-pip
libperl-dev
libssh2-1-dev
libgcrypt20-dev
)
for i in "${pkgs[@]}"; do
......
# We will use pkg-config if available.
include(FindPkgConfig)
if (PKG_CONFIG_FOUND)
pkg_check_modules(LIBSSH2 QUIET "libssh2") # Will be used below.
pkg_check_modules(LIBGCRYPT QUIET "libgcrypt") # Will be used below.
endif ()
# Find libssh2's headers.
if (WITH_LIBSSH2_INCLUDE_DIR)
find_file(
LIBSSH2_HEADER_FOUND
"libssh2.h"
PATHS "${WITH_LIBSSH2_INCLUDE_DIR}"
NO_DEFAULT_PATH)
if (NOT LIBSSH2_HEADER_FOUND)
message(FATAL_ERROR "Could not find libssh2's headers in ${WITH_LIBSSH2_INCLUDE_DIR}.")
endif ()
include_directories("${WITH_LIBSSH2_INCLUDE_DIR}")
elseif (LIBSSH2_FOUND) # Was libssh2 detected with pkg-config ?
if (CMAKE_CXX_FLAGS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LIBSSH2_CFLAGS}")
else ()
set(CMAKE_CXX_FLAGS "${LIBSSH2_CFLAGS}")
endif ()
else ()
find_path(LIBSSH2_INCLUDE_DIR "libssh2.h")
if (NOT LIBSSH2_INCLUDE_DIR)
message(FATAL_ERROR "Could not find libssh2's headers (try WITH_LIBSSH2_INCLUDE_DIR).")
endif ()
include_directories("${LIBSSH2_INCLUDE_DIR}")
endif ()
# Find libssh2's library.
if (WITH_LIBSSH2_LIBRARIES)
set(LIBSSH2_LIBRARIES "${WITH_LIBSSH2_LIBRARIES}")
elseif (WITH_LIBSSH2_LIBRARY_DIR)
find_library(
LIBSSH2_LIBRARIES
"ssh2"
PATHS "${WITH_LIBSSH2_LIBRARY_DIR}"
NO_DEFAULT_PATH)
if (NOT LIBSSH2_LIBRARIES)
message(FATAL_ERROR "Could not find libssh2's library in ${WITH_LIBSSH2_LIBRARY_DIR}.")
endif ()
elseif (LIBSSH2_FOUND) # Was libssh2 detected with pkg-config ?
set(LIBSSH2_LIBRARIES "${LIBSSH2_LDFLAGS}")
else ()
find_library(LIBSSH2_LIBRARIES "ssh2")
if (NOT LIBSSH2_LIBRARIES)
message(FATAL_ERROR "Could not find libssh2's library (try WITH_LIBSSH2_LIBRARY_DIR or WITH_LIBSSH2_LIBRARIES).")
endif ()
endif ()
# Check if libssh2 is using libgcrypt or OpenSSL.
if (WITH_LIBSSH2_WITH_LIBGCRYPT)
set(LIBSSH2_WITH_LIBGCRYPT "${WITH_LIBSSH2_WITH_LIBGCRYPT}")
else ()
set(LIBSSH2_WITH_LIBGCRYPT 1)
endif ()
if (LIBSSH2_WITH_LIBGCRYPT)
# Find libgcrypt's required header.
if (WITH_LIBGCRYPT_INCLUDE_DIR)
find_file(
LIBGCRYPT_HEADER_FOUND
"gcrypt.h"
PATHS "${WITH_LIBGCRYPT_INCLUDE_DIR}"
NO_DEFAULT_PATH)
if (NOT LIBGCRYPT_HEADER_FOUND)
message(FATAL_ERROR "Could not find libgcrypt's headers in ${WITH_LIBGCRYPT_INCLUDE_DIR}.")
endif ()
include_directories("${WITH_LIBGCRYPT_INCLUDE_DIR}")
else ()
find_file(LIBGCRYPT_HEADER_FOUND "gcrypt.h")
if (NOT LIBGCRYPT_HEADER_FOUND)
message(FATAL_ERROR "Could not find libgcrypt's headers (try WITH_LIBGCRYPT_INCLUDE_DIR).")
endif ()
endif ()
# Find libgcrypt's library.
if (WITH_LIBGCRYPT_LIBRARIES)
set(LIBGCRYPT_LIBRARIES "${WITH_LIBGCRYPT_LIBRARIES}")
elseif (WITH_LIBGCRYPT_LIBRARY_DIR)
find_library(
LIBGCRYPT_LIBRARIES
"gcrypt"
PATHS "${WITH_LIBGCRYPT_LIBRARY_DIR}"
NO_DEFAULT_PATH)
if (NOT LIBGCRYPT_LIBRARIES)
message(FATAL_ERROR "Could not find libgcrypt's library in ${WITH_LIBGCRYPT_LIBRARY_DIR}.")
endif ()
elseif (LIBGCRYPT_FOUND) # Was libgcrypt detected with pkg-config ?
set(LIBGCRYPT_LIBRARIES "${LIBGCRYPT_LDFLAGS}")
else ()
find_library(LIBGCRYPT_LIBRARIES "gcrypt")
if (NOT LIBGCRYPT_LIBRARIES)
message(FATAL_ERROR "Could not find libgcrypt's library (try WITH_LIBGCRYPT_LIBRARY_DIR).")
endif ()
endif ()
# Add macro.
add_definitions(-DLIBSSH2_WITH_LIBGCRYPT)
endif ()
......@@ -2,7 +2,15 @@
gtest/cci.20210126
fmt/7.1.3
spdlog/1.8.5
asio/1.21.0
boost/1.78.0
libssh2/1.10.0
[generators]
cmake_paths
cmake_find_package
[options]
boost:header_only=True
#libssh2:enable_debug_logging=True
libssh2:shared=False
\ No newline at end of file
......@@ -46,9 +46,7 @@ class check : public handle_listener {
check(check const& c) = delete;
check& operator=(check const& c) = delete;
void error(handle& h) override;
pid_t execute(uint64_t cmd_id,
std::string const& cmd,
const timestamp& tmt);
pid_t execute(uint64_t cmd_id, std::string const& cmd, const timestamp& tmt);
void listen(listener* listnr);
void on_timeout(bool final = true);
void read(handle& h) override;
......@@ -58,7 +56,6 @@ class check : public handle_listener {
void write(handle& h) override;
private:
void _send_result_and_unregister(result const& r);
pid_t _child;
......
......@@ -89,8 +89,7 @@ pid_t check::execute(uint64_t cmd_id,
_err.set_fd(fds[2]);
// Store command ID.
log::core()->debug("check {0} has ID {1}", static_cast<void*>(this),
cmd_id);
log::core()->debug("check {0} has ID {1}", static_cast<void*>(this), cmd_id);
_cmd_id = cmd_id;
// Register with multiplexer.
......@@ -112,8 +111,8 @@ pid_t check::execute(uint64_t cmd_id,
* @param[in] listnr New listener.
*/
void check::listen(listener* listnr) {
log::core()->debug("check {0} is listened by {1}",
static_cast<void*>(this), static_cast<void*>(listnr));
log::core()->debug("check {0} is listened by {1}", static_cast<void*>(this),
static_cast<void*>(listnr));
_listnr = listnr;
}
......@@ -212,7 +211,7 @@ void check::terminated(int exit_code) {
*/
void check::unlisten(listener* listnr) {
log::core()->debug("listener {0} stops listening check {1}",
static_cast<void*>(_listnr), static_cast<void*>(this));
static_cast<void*>(_listnr), static_cast<void*>(this));
_listnr = nullptr;
}
......
......@@ -22,7 +22,8 @@ if (WITH_KNOWN_HOSTS_CHECK)
add_definitions(-DWITH_KNOWN_HOSTS_CHECK)
endif ()
include_directories(${CMAKE_SOURCE_DIR}/ssh/inc)
include_directories(${CMAKE_SOURCE_DIR}/ssh/inc ${Boost_INCLUDE_DIRS} ${Libssh2_INCLUDE_DIR})
# SSH connector.
add_executable(centreon_connector_ssh
# Sources.
......@@ -30,8 +31,6 @@ add_executable(centreon_connector_ssh
${CMAKE_SOURCE_DIR}/ssh/src/main.cc
${CMAKE_SOURCE_DIR}/ssh/src/checks/check.cc
${CMAKE_SOURCE_DIR}/ssh/src/checks/result.cc
${CMAKE_SOURCE_DIR}/ssh/src/checks/timeout.cc
${CMAKE_SOURCE_DIR}/ssh/src/multiplexer.cc
${CMAKE_SOURCE_DIR}/ssh/src/options.cc
${CMAKE_SOURCE_DIR}/ssh/src/orders/parser.cc
${CMAKE_SOURCE_DIR}/ssh/src/orders/options.cc
......@@ -39,27 +38,25 @@ add_executable(centreon_connector_ssh
${CMAKE_SOURCE_DIR}/ssh/src/reporter.cc
${CMAKE_SOURCE_DIR}/ssh/src/sessions/credentials.cc
${CMAKE_SOURCE_DIR}/ssh/src/sessions/session.cc
${CMAKE_SOURCE_DIR}/ssh/src/sessions/socket_handle.cc
# Headers.
${CMAKE_SOURCE_DIR}/ssh/inc/com/centreon/connector/ssh/checks/check.hh
${CMAKE_SOURCE_DIR}/ssh/inc/com/centreon/connector/ssh/checks/listener.hh
${CMAKE_SOURCE_DIR}/ssh/inc/com/centreon/connector/ssh/checks/result.hh
${CMAKE_SOURCE_DIR}/ssh/inc/com/centreon/connector/ssh/checks/timeout.hh
${CMAKE_SOURCE_DIR}/ssh/inc/com/centreon/connector/ssh/multiplexer.hh
${CMAKE_SOURCE_DIR}/ssh/inc/com/centreon/connector/ssh/namespace.hh
${CMAKE_SOURCE_DIR}/ssh/inc/com/centreon/connector/ssh/options.hh
${CMAKE_SOURCE_DIR}/ssh/inc/com/centreon/connector/ssh/orders/listener.hh
${CMAKE_SOURCE_DIR}/ssh/inc/com/centreon/connector/ssh/orders/parser.hh
${CMAKE_SOURCE_DIR}/ssh/inc/com/centreon/connector/ssh/orders/options.hh
${CMAKE_SOURCE_DIR}/ssh/inc/com/centreon/connector/ssh/policy.hh
${CMAKE_SOURCE_DIR}/ssh/inc/com/centreon/connector/ssh/reporter.hh
${CMAKE_SOURCE_DIR}/ssh/inc/com/centreon/connector/ssh/sessions/credentials.hh
${CMAKE_SOURCE_DIR}/ssh/inc/com/centreon/connector/ssh/sessions/listener.hh
${CMAKE_SOURCE_DIR}/ssh/inc/com/centreon/connector/ssh/sessions/session.hh
${CMAKE_SOURCE_DIR}/ssh/inc/com/centreon/connector/ssh/sessions/socket_handle.hh
)
target_link_libraries(centreon_connector_ssh ${LIBSSH2_LIBRARIES}
${CLIB_LIBRARIES} ${LIBGCRYPT_LIBRARIES} ${spdlog_LIBS} ${fmt_LIBS} pthread)
target_link_libraries(centreon_connector_ssh ${Libssh2_LIBRARIES}
${CLIB_LIBRARIES} ${spdlog_LIBS} ${fmt_LIBS} pthread)
target_precompile_headers(centreon_connector_ssh PRIVATE precomp_inc/precomp.hh)
# Installation path.
if (WITH_PREFIX)
......
......@@ -19,17 +19,15 @@
#ifndef CCCS_CHECKS_CHECK_HH
#define CCCS_CHECKS_CHECK_HH
#include <ctime>
#include <list>
#include <string>
#include "com/centreon/connector/ssh/checks/listener.hh"
#include "com/centreon/connector/ssh/checks/result.hh"
#include "com/centreon/connector/ssh/namespace.hh"
#include "com/centreon/connector/ssh/sessions/listener.hh"
#include "com/centreon/connector/ssh/sessions/session.hh"
#include "com/centreon/timestamp.hh"
CCCS_BEGIN()
namespace sessions {
class session;
}
namespace checks {
// Forward declaration.
class result;
......@@ -40,45 +38,68 @@ class result;
*
* Execute a check by opening a new channel on a SSH session.
*/
class check : public sessions::listener {
class check : public std::enable_shared_from_this<check> {
using callback = std::function<void(const result&)>;
public:
check(int skip_stdout = -1, int skip_stderr = -1);
~check() noexcept override;
void execute(sessions::session& sess,
unsigned long long cmd_id,
std::list<std::string> const& cmds,
const timestamp& tmt);
void listen(checks::listener* listnr);
void on_available(sessions::session& sess) override;
void on_close(sessions::session& sess) override;
void on_connected(sessions::session& sess) override;
void on_timeout();
void unlisten(checks::listener* listnr);
using pointer = std::shared_ptr<check>;
using string_list = std::list<std::string>;
check(const std::shared_ptr<sessions::session>& session,
unsigned long long cmd_id,
string_list const& cmds,
const time_point& tmt,
int skip_stdout = -1,
int skip_stderr = -1);
~check() noexcept;
template <class callback_type>
void execute(callback_type&& callback);
void dump(std::ostream& s) const;
private:
enum e_step { chan_open = 1, chan_exec, chan_read, chan_close };
check(check const& c);
check& operator=(check const& c);
bool _close();
bool _exec();
bool _open();
bool _read();
void _send_result_and_unregister(result const& r);
enum class e_step {
chan_open = 1,
chan_exec,
chan_read_stdout,
chan_read_stderr,
chan_close
};
check(check const& c) = delete;
check& operator=(check const& c) = delete;
void _execute();
void _process();
void _close();
void _close_handler(int retval);
void _exec();
void _open();
void _read(bool read_stdout);
static std::string& _skip_data(std::string& data, int nb_line);
LIBSSH2_CHANNEL* _channel;
std::list<std::string> _cmds;
unsigned long long _cmd_id;
checks::listener* _listnr;
sessions::session* _session;
string_list _cmds;
time_point _timeout;
std::shared_ptr<sessions::session> _session;
callback _callback;
int _skip_stderr;
int _skip_stdout;
std::string _stderr;
std::string _stdout;
e_step _step;
unsigned long _timeout;
};
template <class callback_type>
void check::execute(callback_type&& callback) {
_callback = callback;
_execute();
}
std::ostream& operator<<(std::ostream& os, const check& chk);
} // namespace checks
CCCS_END()
......
......@@ -34,7 +34,13 @@ namespace checks {
class result {
public:
result();
result(unsigned long long cmd_id, int exit_code, const std::string& error);
result(unsigned long long cmd_id,
int exit_code,
const std::string& output,
const std::string& error);
result(result const& r);
~result() = default;
result& operator=(result const& r);
unsigned long long get_command_id() const noexcept;
......
/*
** Copyright 2011-2013 Centreon
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
**
** For more information : contact@centreon.com
*/
#ifndef CCCS_CHECKS_TIMEOUT_HH
#define CCCS_CHECKS_TIMEOUT_HH
#include <cstddef>
#include "com/centreon/connector/ssh/namespace.hh"
#include "com/centreon/task.hh"
CCCS_BEGIN()
namespace checks {
// Forward declaration.
class check;
/**
* @class timeout timeout.hh "com/centreon/connector/ssh/checks/timeout.hh"
* @brief Check timeout.
*
* Task executed when a check timeouts.
*/
class timeout : public com::centreon::task {
check* _check;
public:
timeout(check* chk = nullptr);
~timeout() noexcept override = default;
timeout(timeout const& t) = delete;
timeout& operator=(timeout const& t) = delete;
check* get_check() const noexcept;
void run() override;
};
} // namespace checks
CCCS_END()
#endif // !CCCS_CHECKS_TIMEOUT_HH
/*
** Copyright 2011-2013 Centreon
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
**
** For more information : contact@centreon.com
*/
#ifndef CCCS_MULTIPLEXER_HH
#define CCCS_MULTIPLEXER_HH
#include "com/centreon/connector/ssh/namespace.hh"
#include "com/centreon/handle_manager.hh"
#include "com/centreon/task_manager.hh"
CCCS_BEGIN()
/**
* @class multiplexer multiplexer.hh
* "com/centreon/connector/ssh/multiplexer.hh"
* @brief Multiplexing class.
*
* Singleton that aggregates multiplexing features such as file
* descriptor monitoring and task execution.
*/
class multiplexer : public com::centreon::task_manager,
public com::centreon::handle_manager {
multiplexer();
public:
static multiplexer& instance() noexcept;
multiplexer(multiplexer const& m) = delete;
multiplexer& operator=(multiplexer const& m) = delete;
static void load();
static void unload();
};
CCCS_END()
#endif // !CCCS_MULTIPLEXER_HH
/*
** Copyright 2011-2013 Centreon
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
**
** For more information : contact@centreon.com
*/
#ifndef CCCS_ORDERS_LISTENER_HH
#define CCCS_ORDERS_LISTENER_HH
#include <ctime>
#include <list>
#include <string>
#include "com/centreon/connector/ssh/namespace.hh"
#include "com/centreon/timestamp.hh"
CCCS_BEGIN()
namespace orders {
/**
* @class listener listener.hh "com/centreon/connector/ssh/orders/listener.hh"
* @brief Listen orders issued by the monitoring engine.
*
* Wait for orders from the monitoring engine and take actions
* accordingly.
*/
class listener {
public:
listener() = default;
listener(listener const& l) = delete;
virtual ~listener() = default;
listener& operator=(listener const& l) = delete;
virtual void on_eof() = 0;
virtual void on_error(uint64_t cmd_id, char const* msg) = 0;
virtual void on_execute(uint64_t cmd_id,
const timestamp& timeout,
std::string const& host,
unsigned short port,
std::string const& user,
std::string const& password,
std::string const& identity,
std::list<std::string> const& cmds,
int skip_stdout,
int skip_stderr,
bool is_ipv6) = 0;
virtual void on_quit() = 0;
virtual void on_version() = 0;
};
} // namespace orders
CCCS_END()
#endif // !CCCS_ORDERS_LISTENER_HH