From 85c81304727b349aff49b040e7d6300c0edbc147 Mon Sep 17 00:00:00 2001 From: Jean Christophe Roques Date: Wed, 24 Jan 2024 16:31:23 +0100 Subject: [PATCH] add class process to common --- common/CMakeLists.txt | 1 + .../com/centreon/common/http/http_client.hh | 6 +- .../com/centreon/common/http/http_server.hh | 6 +- common/http/src/http_client.cc | 30 +- common/http/src/http_server.cc | 8 +- common/inc/com/centreon/common/process.hh | 151 ++++++++ .../com/centreon/common/rapidjson_helper.hh | 1 + common/precomp_inc/precomp.hh | 2 + common/src/process.cc | 333 ++++++++++++++++++ common/test/CMakeLists.txt | 1 + common/test/process_test.cc | 175 +++++++++ common/test/test_main.cc | 1 + engine/modules/opentelemetry/CMakeLists.txt | 5 - .../opentelemetry/src/otl_converter.cc | 2 +- vcpkg.json | 3 +- 15 files changed, 693 insertions(+), 32 deletions(-) create mode 100644 common/inc/com/centreon/common/process.hh create mode 100644 common/src/process.cc create mode 100644 common/test/process_test.cc diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 5c3de9473bc..3e79205c8ec 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -45,6 +45,7 @@ add_custom_command( set(SOURCES ${SRC_DIR}/hex_dump.cc ${SRC_DIR}/pool.cc + ${SRC_DIR}/process.cc ${SRC_DIR}/process_stat.cc ${SRC_DIR}/process_stat.pb.cc ${SRC_DIR}/process_stat.grpc.pb.cc diff --git a/common/http/inc/com/centreon/common/http/http_client.hh b/common/http/inc/com/centreon/common/http/http_client.hh index 4bbe4a1f6c2..549e594a7cd 100644 --- a/common/http/inc/com/centreon/common/http/http_client.hh +++ b/common/http/inc/com/centreon/common/http/http_client.hh @@ -104,9 +104,9 @@ class client : public std::enable_shared_from_this { protected: client(const std::shared_ptr& io_context, - const std::shared_ptr& logger, - const http_config::pointer& conf, - connection_creator conn_creator); + const std::shared_ptr& logger, + const http_config::pointer& conf, + connection_creator conn_creator); public: using pointer = std::shared_ptr; diff --git a/common/http/inc/com/centreon/common/http/http_server.hh b/common/http/inc/com/centreon/common/http/http_server.hh index 7c9d680083b..0e41b57daf2 100644 --- a/common/http/inc/com/centreon/common/http/http_server.hh +++ b/common/http/inc/com/centreon/common/http/http_server.hh @@ -49,9 +49,9 @@ class server : public std::enable_shared_from_this { using pointer = std::shared_ptr; server(const std::shared_ptr& io_context, - const std::shared_ptr& logger, - const http_config::pointer& conf, - connection_creator&& conn_creator); + const std::shared_ptr& logger, + const http_config::pointer& conf, + connection_creator&& conn_creator); ~server(); diff --git a/common/http/src/http_client.cc b/common/http/src/http_client.cc index c7eaffa08e6..6dc9e13d49e 100644 --- a/common/http/src/http_client.cc +++ b/common/http/src/http_client.cc @@ -37,9 +37,9 @@ using lock_guard = std::lock_guard; * server, it can be a http_connection::load, https_connection::load.... */ client::client(const std::shared_ptr& io_context, - const std::shared_ptr& logger, - const http_config::pointer& conf, - connection_creator conn_creator) + const std::shared_ptr& logger, + const http_config::pointer& conf, + connection_creator conn_creator) : _io_context(io_context), _logger(logger), _conf(conf), @@ -84,7 +84,7 @@ client::pointer client::load( * @return false if enqueue */ bool client::_send_or_push(const cb_request::pointer request, - bool push_to_front) { + bool push_to_front) { if (_halt) { return false; } @@ -199,7 +199,7 @@ void client::_send_first_queue_request() { * @param conn */ void client::_send(const cb_request::pointer& request, - connection_base::pointer conn) { + connection_base::pointer conn) { if (_logger->level() == spdlog::level::trace) { SPDLOG_LOGGER_TRACE(_logger, "send {} on {:p}", *request->request, static_cast(conn.get())); @@ -226,9 +226,9 @@ void client::_send(const cb_request::pointer& request, * @param conn */ void client::_on_connect(const boost::beast::error_code& error, - const std::string& detail, - const cb_request::pointer& request, - connection_base::pointer conn) { + const std::string& detail, + const cb_request::pointer& request, + connection_base::pointer conn) { if (error) { // error => shutdown and retry SPDLOG_LOGGER_ERROR(_logger, "{:p} fail to connect {}: {}", static_cast(conn.get()), error.message(), @@ -264,10 +264,10 @@ void client::_on_connect(const boost::beast::error_code& error, * @param conn */ void client::_on_sent(const boost::beast::error_code& error, - const std::string& detail, - const cb_request::pointer& request, - const response_ptr& response, - connection_base::pointer conn) { + const std::string& detail, + const cb_request::pointer& request, + const response_ptr& response, + connection_base::pointer conn) { cb_request::pointer to_call; if (error) { // error => shutdown and _retry SPDLOG_LOGGER_ERROR(_logger, "{:p} fail to send request", @@ -363,9 +363,9 @@ void client::shutdown() { * @param response */ void client::_retry(const boost::beast::error_code& error, - const std::string& detail, - const cb_request::pointer& request, - const response_ptr& response) { + const std::string& detail, + const cb_request::pointer& request, + const response_ptr& response) { cb_request::pointer to_call; if (_halt) { // object halted => callback without _retry diff --git a/common/http/src/http_server.cc b/common/http/src/http_server.cc index 920b98826a0..37ba2bf5dd5 100644 --- a/common/http/src/http_server.cc +++ b/common/http/src/http_server.cc @@ -22,9 +22,9 @@ using namespace com::centreon::common::http; server::server(const std::shared_ptr& io_context, - const std::shared_ptr& logger, - const http_config::pointer& conf, - connection_creator&& conn_creator) + const std::shared_ptr& logger, + const http_config::pointer& conf, + connection_creator&& conn_creator) : _io_context(io_context), _logger(logger), _conf(conf), @@ -78,7 +78,7 @@ void server::start_accept() { } void server::on_accept(const boost::beast::error_code& err, - const connection_base::pointer& conn) { + const connection_base::pointer& conn) { if (err) { if (err != boost::asio::error::operation_aborted) { SPDLOG_LOGGER_ERROR(_logger, "fail accept connection on {}: {}", diff --git a/common/inc/com/centreon/common/process.hh b/common/inc/com/centreon/common/process.hh new file mode 100644 index 00000000000..2ffa312c325 --- /dev/null +++ b/common/inc/com/centreon/common/process.hh @@ -0,0 +1,151 @@ +/* +** Copyright 2024 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 CENTREON_AGENT_CHECK_PROCESS_HH +#define CENTREON_AGENT_CHECK_PROCESS_HH + +namespace com::centreon::common { + +namespace detail { +// here to limit included files +struct boost_process; +} // namespace detail + +/** + * @brief This class allow to exec a process asynchronously. + * It's a base class. If you want to get stdin and stdout returned data, you + * must inherit from this and override on_stdout_read and on_stderr_read + * You can call start_process at any moment, if a process is already running, + * it's killed + * As we can start a process at any moment, all handlers take a caller in + * parameter, if this caller is not equal to current _proc, we do nothing. + * When handler like on_stdout_read are called, _protect is already locked + */ +class process : public std::enable_shared_from_this { + std::shared_ptr _io_context; + std::shared_ptr _logger; + + std::string _exe_path; + std::vector _args; + + std::deque> _stdin_write_queue; + bool _write_pending = false; + + // it would be better to user an unique_ptr but gcc complains because he + // doesn't know boost_process size + detail::boost_process* _proc = nullptr; + + int _exit_status = 0; + + std::mutex _protect; + + void stdin_write_no_lock(const std::shared_ptr& data); + void stdin_write(const std::shared_ptr& data); + + void stdout_read(); + void stderr_read(); + + protected: + char _stdout_read_buffer[0x1000]; + char _stderr_read_buffer[0x1000]; + + virtual void on_stdout_read(const boost::system::error_code& err, + size_t nb_read); + virtual void on_stderr_read(const boost::system::error_code& err, + size_t nb_read); + + virtual void on_process_end(const boost::system::error_code& err, + int raw_exit_status); + + virtual void on_stdin_write(const boost::system::error_code& err); + + public: + template + process(const std::shared_ptr& io_context, + const std::shared_ptr& logger, + const std::string& exe_path, + string_iterator arg_begin, + string_iterator arg_end); + + template + process(const std::shared_ptr& io_context, + const std::shared_ptr& logger, + const std::string& exe_path, + const args_container& args); + + template + process(const std::shared_ptr& io_context, + const std::shared_ptr& logger, + const std::string& exe_path, + const std::initializer_list& args); + + process(const std::shared_ptr& io_context, + const std::shared_ptr& logger, + const std::string& cmd_line); + + virtual ~process(); + + template + void write_to_stdin(const string_class& content); + + void start_process(); + + void kill(); + + int get_exit_status() const { return _exit_status; } +}; + +template +process::process(const std::shared_ptr& io_context, + const std::shared_ptr& logger, + const std::string& exe_path, + string_iterator arg_begin, + string_iterator arg_end) + : _io_context(io_context), + _logger(logger), + _exe_path(exe_path), + _args(arg_begin, arg_end) {} + +template +process::process(const std::shared_ptr& io_context, + const std::shared_ptr& logger, + const std::string& exe_path, + const args_container& args) + : _io_context(io_context), + _logger(logger), + _exe_path(exe_path), + _args(args) {} + +template +process::process(const std::shared_ptr& io_context, + const std::shared_ptr& logger, + const std::string& exe_path, + const std::initializer_list& args) + : _io_context(io_context), _logger(logger), _exe_path(exe_path) { + _args.reserve(args.size()); + for (const auto& str : args) { + _args.emplace_back(str); + } +} + +template +void process::write_to_stdin(const string_class& content) { + stdin_write(std::make_shared(content)); +} + +} // namespace com::centreon::common +#endif diff --git a/common/inc/com/centreon/common/rapidjson_helper.hh b/common/inc/com/centreon/common/rapidjson_helper.hh index ef33c94a571..a7039dfeea5 100644 --- a/common/inc/com/centreon/common/rapidjson_helper.hh +++ b/common/inc/com/centreon/common/rapidjson_helper.hh @@ -285,6 +285,7 @@ class rapidjson_helper { // as overriding can't be done with returned type, we use a templated method template value_type get(const char* field_name); + const rapidjson::Value& get_member(const char* field_name) const; /** diff --git a/common/precomp_inc/precomp.hh b/common/precomp_inc/precomp.hh index 1c12aaeb5eb..09b3a7e6806 100644 --- a/common/precomp_inc/precomp.hh +++ b/common/precomp_inc/precomp.hh @@ -25,8 +25,10 @@ #include #include #include +#include #include #include +#include #include #include #include diff --git a/common/src/process.cc b/common/src/process.cc new file mode 100644 index 00000000000..48af76e6ba5 --- /dev/null +++ b/common/src/process.cc @@ -0,0 +1,333 @@ +/* + * Copyright 2024 Centreon + * + * This file is part of Centreon Engine. + * + * Centreon Engine is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * Centreon Engine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Centreon Engine. If not, see + * . + */ + +#include +#include + +#include "process.hh" + +namespace proc = boost::process::v2; + +namespace com::centreon::common::detail { +/** + * @brief each time we start a process we create this struct witch contains all + * sub-process objects + * + */ +struct boost_process { + boost_process(asio::io_context& io_context, + const std::string& exe_path, + const std::vector& args) + : stdout(io_context), + stderr(io_context), + stdin(io_context), + proc(io_context, + exe_path, + args, + proc::process_stdio{stdin, stdout, stderr}) {} + + asio::readable_pipe stdout; + asio::readable_pipe stderr; + asio::writable_pipe stdin; + proc::process proc; +}; +} // namespace com::centreon::common::detail + +using namespace com::centreon::common; + +/** + * @brief Construct a new process::process object + * + * @param io_context + * @param logger + * @param cmd_line cmd line split (the first element is the path of the + * executable) + */ +process::process(const std::shared_ptr& io_context, + const std::shared_ptr& logger, + const std::string& cmd_line) + : _io_context(io_context), _logger(logger) { + auto split_res = + absl::StrSplit(cmd_line, absl::ByAnyChar(" \t"), absl::SkipEmpty()); + if (split_res.begin() == split_res.end()) { + SPDLOG_LOGGER_ERROR(_logger, "empty command line:\"{}\"", cmd_line); + throw exceptions::msg_fmt("empty command line:\"{}\"", cmd_line); + } + auto field_iter = split_res.begin(); + + _exe_path = *field_iter++; + for (; field_iter != split_res.end(); ++field_iter) { + _args.emplace_back(*field_iter); + } +} + +/** + * @brief Destroy the process::process object + * + */ +process::~process() { + if (_proc) + delete _proc; +} + +/** + * @brief start a new process, if a previous one is running, it's killed + * In this function, we start child process and stdout, stderr asynchronous read + * we also start an asynchronous read on process fd to be aware of child process + * termination + */ +void process::start_process() { + SPDLOG_LOGGER_DEBUG(_logger, "start process: {}", _exe_path); + std::lock_guard l(_protect); + _stdin_write_queue.clear(); + _write_pending = false; + + if (_proc) { + delete _proc; + } + try { + _proc = new detail::boost_process(*_io_context, _exe_path, _args); + _proc->proc.async_wait( + [me = shared_from_this(), current = _proc]( + const boost::system::error_code& err, int raw_exit_status) { + std::lock_guard l(me->_protect); + if (current != me->_proc) { + return; + } + me->on_process_end(err, raw_exit_status); + }); + } catch (const std::exception& e) { + SPDLOG_LOGGER_ERROR(_logger, "fail to start {}: {}", _exe_path, e.what()); + throw; + } + stdout_read(); + stderr_read(); +} + +/** + * @brief called when child process end + * + * @param err + * @param raw_exit_status end status of the process + */ +void process::on_process_end(const boost::system::error_code& err, + int raw_exit_status) { + if (err) { + SPDLOG_LOGGER_ERROR(_logger, "fail async_wait of {}: {}", _exe_path, + err.message()); + _exit_status = -1; + } else { + _exit_status = proc::evaluate_exit_code(raw_exit_status); + SPDLOG_LOGGER_DEBUG(_logger, "end of process {}, exit_status={}", _exe_path, + _exit_status); + } +} + +/** + * @brief kill child process + * + */ +void process::kill() { + std::lock_guard l(_protect); + if (_proc) { + boost::system::error_code err; + _proc->proc.terminate(err); + delete _proc; + _proc = nullptr; + } +} + +/** + * @brief write some data to child process stdin, if a write is pending, data is + * pushed to a queue + * + * @param data + */ +void process::stdin_write(const std::shared_ptr& data) { + std::lock_guard l(_protect); + stdin_write_no_lock(data); +} + +/** + * @brief asynchronously write some data to child process stdin, if a write is + * pending, data is pushed to a queue + * + * @param data + */ +void process::stdin_write_no_lock(const std::shared_ptr& data) { + if (!_proc) { + SPDLOG_LOGGER_ERROR(_logger, "stdin_write process {} not started", + _exe_path); + throw exceptions::msg_fmt("stdin_write process {} not started", _exe_path); + } + if (_write_pending) { + _stdin_write_queue.push_back(data); + } else { + try { + _write_pending = true; + _proc->stdin.async_write_some( + asio::buffer(*data), + [me = shared_from_this(), caller = _proc, data]( + const boost::system::error_code& err, size_t nb_written) { + std::lock_guard l(me->_protect); + if (caller != me->_proc) { + return; + } + me->on_stdin_write(err); + }); + } catch (const std::exception& e) { + _write_pending = false; + SPDLOG_LOGGER_ERROR(_logger, + "stdin_write process {} fail to write to stdin {}", + _exe_path, e.what()); + } + } +} + +/** + * @brief stdin write handler + * if data remains in queue, we send them + * if override process::on_stdin_write must be called + * + * @param err + */ +void process::on_stdin_write(const boost::system::error_code& err) { + _write_pending = false; + + if (err) { + if (err == asio::error::eof) { + SPDLOG_LOGGER_DEBUG(_logger, + "on_stdin_write process {} fail to write to stdin {}", + _exe_path, err.message()); + } else { + SPDLOG_LOGGER_ERROR(_logger, + "on_stdin_write process {} fail to write to stdin {}", + _exe_path, err.message()); + } + return; + } + + if (!_stdin_write_queue.empty()) { + std::shared_ptr to_send = _stdin_write_queue.front(); + _stdin_write_queue.pop_front(); + stdin_write_no_lock(to_send); + } +} + +/** + * @brief asynchronous read from child process stdout + * + */ +void process::stdout_read() { + if (_proc) { + try { + _proc->stdout.async_read_some( + asio::buffer(_stdout_read_buffer), + [me = shared_from_this(), caller = _proc]( + const boost::system::error_code& err, size_t nb_read) { + std::lock_guard l(me->_protect); + if (caller != me->_proc) { + return; + } + me->on_stdout_read(err, nb_read); + }); + } catch (const std::exception& e) { + _io_context->post([me = shared_from_this(), caller = _proc]() { + std::lock_guard l(me->_protect); + me->on_stdout_read(std::make_error_code(std::errc::broken_pipe), 0); + }); + } + } +} + +/** + * @brief stdout read handler + * This method or his override is called with _protect locked. + * If override process::on_stdout_read must be called + * + * @param err + * @param nb_read + */ +void process::on_stdout_read(const boost::system::error_code& err, + size_t nb_read) { + if (err) { + if (err == asio::error::eof) { + SPDLOG_LOGGER_DEBUG(_logger, "fail read from stdout of process {}: {}", + _exe_path, err.message()); + } else { + SPDLOG_LOGGER_ERROR(_logger, "fail read from stdout of process {}: {}", + _exe_path, err.message()); + } + return; + } + SPDLOG_LOGGER_TRACE(_logger, " process: {} read from stdout: {}", _exe_path, + std::string_view(_stdout_read_buffer, nb_read)); + stdout_read(); +} + +/** + * @brief asynchronous read from child process stderr + * + */ +void process::stderr_read() { + if (_proc) { + try { + _proc->stderr.async_read_some( + asio::buffer(_stderr_read_buffer), + [me = shared_from_this(), caller = _proc]( + const boost::system::error_code& err, size_t nb_read) { + std::lock_guard l(me->_protect); + if (caller != me->_proc) { + return; + } + me->on_stderr_read(err, nb_read); + }); + } catch (const std::exception& e) { + _io_context->post([me = shared_from_this(), caller = _proc]() { + std::lock_guard l(me->_protect); + me->on_stderr_read(std::make_error_code(std::errc::broken_pipe), 0); + }); + } + } +} + +/** + * @brief stderr read handler + * This method or his override is called with _protect locked. + * If override process::on_stderr_read must be called + * + * @param err + * @param nb_read + */ +void process::on_stderr_read(const boost::system::error_code& err, + size_t nb_read) { + if (err) { + if (err == asio::error::eof) { + SPDLOG_LOGGER_DEBUG(_logger, "fail read from stderr of process {}: {}", + _exe_path, err.message()); + } else { + SPDLOG_LOGGER_ERROR(_logger, "fail read from stderr of process {}: {}", + _exe_path, err.message()); + } + } else { + SPDLOG_LOGGER_TRACE(_logger, " process: {} read from stdout: {}", _exe_path, + std::string_view(_stderr_read_buffer, nb_read)); + stderr_read(); + } +} diff --git a/common/test/CMakeLists.txt b/common/test/CMakeLists.txt index 8c2861cc222..9b7527619d9 100644 --- a/common/test/CMakeLists.txt +++ b/common/test/CMakeLists.txt @@ -22,6 +22,7 @@ add_executable(ut_common process_stat_test.cc hex_dump_test.cc node_allocator_test.cc + process_test.cc rapidjson_helper_test.cc test_main.cc ${TESTS_SOURCES}) diff --git a/common/test/process_test.cc b/common/test/process_test.cc new file mode 100644 index 00000000000..ba4e0a99f2a --- /dev/null +++ b/common/test/process_test.cc @@ -0,0 +1,175 @@ +/** + * Copyright 2023 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 + */ + +#include +#include + +#include "pool.hh" +#include "process.hh" + +using namespace com::centreon::common; + +extern std::shared_ptr g_io_context; + +static std::shared_ptr _logger = + spdlog::stdout_color_mt("process_test"); + +class process_test : public ::testing::Test { + public: + static void SetUpTestSuite() { + _logger->set_level(spdlog::level::trace); + _logger->set_pattern("[%Y-%m-%dT%H:%M:%S.%e%z] [%s:%#] [%n] [%l] [%P] %v"); + } +}; + +class process_wait : public process { + std::condition_variable _cond; + std::string _stdout; + std::string _stderr; + + public: + void on_stdout_read(const boost::system::error_code& err, + size_t nb_read) override { + if (!err) { + _stdout += std::string_view(_stdout_read_buffer, nb_read); + } + process::on_stdout_read(err, nb_read); + } + + void on_stderr_read(const boost::system::error_code& err, + size_t nb_read) override { + if (!err) { + _stderr += std::string_view(_stderr_read_buffer, nb_read); + } + process::on_stderr_read(err, nb_read); + } + + void on_process_end(const boost::system::error_code& err, + int raw_exit_status) override { + process::on_process_end(err, raw_exit_status); + _cond.notify_one(); + } + + template + process_wait(const std::shared_ptr& io_context, + const std::shared_ptr& logger, + const std::string& exe_path, + const std::initializer_list& args) + : process(io_context, logger, exe_path, args) {} + + process_wait(const std::shared_ptr& io_context, + const std::shared_ptr& logger, + const std::string& cmd_line) + : process(io_context, logger, cmd_line) {} + + const std::string& get_stdout() const { return _stdout; } + const std::string& get_stderr() const { return _stderr; } + + void wait() { + std::mutex dummy; + std::unique_lock l(dummy); + _cond.wait(l); + } +}; + +TEST_F(process_test, echo) { + using namespace std::literals; + std::shared_ptr to_wait( + new process_wait(g_io_context, _logger, "/usr/bin/echo", {"hello"s})); + to_wait->start_process(); + to_wait->wait(); + ASSERT_EQ(to_wait->get_exit_status(), 0); + ASSERT_EQ(to_wait->get_stdout(), "hello\n"); + ASSERT_EQ(to_wait->get_stderr(), ""); +} + +TEST_F(process_test, throw_on_error) { + using namespace std::literals; + std::shared_ptr to_wait( + new process_wait(g_io_context, _logger, "turlututu", {"hello"s})); + ASSERT_THROW(to_wait->start_process(), std::exception); +} + +TEST_F(process_test, script_error) { + using namespace std::literals; + std::shared_ptr to_wait( + new process_wait(g_io_context, _logger, "/bin/sh", {"taratata"s})); + to_wait->start_process(); + to_wait->wait(); + ASSERT_EQ(to_wait->get_exit_status(), 2); + ASSERT_EQ(to_wait->get_stdout(), ""); + ASSERT_EQ(to_wait->get_stderr(), + "/bin/sh: 0: cannot open taratata: No such file\n"); +} + +TEST_F(process_test, call_start_several_time) { + std::shared_ptr to_wait( + new process_wait(g_io_context, _logger, "/usr/bin/echo", {"hello"})); + std::string expected; + for (int ii = 0; ii < 10; ++ii) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + to_wait->start_process(); + expected += "hello\n"; + } + to_wait->wait(); + ASSERT_EQ(to_wait->get_exit_status(), 0); + ASSERT_EQ(to_wait->get_stdout(), expected); + ASSERT_EQ(to_wait->get_stderr(), ""); +} + +TEST_F(process_test, stdin_to_stdout) { + ::remove("toto.sh"); + std::ofstream script("toto.sh"); + script << "while read line ; do echo receive $line ; done" << std::endl; + + std::shared_ptr loopback( + new process_wait(g_io_context, _logger, "/bin/sh toto.sh")); + + loopback->start_process(); + + std::string expected; + for (unsigned ii = 0; ii < 10; ++ii) { + if (ii > 5) { + // in order to let some async_read_some complete + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + loopback->write_to_stdin(fmt::format("hello{}\n", ii)); + expected += fmt::format("receive hello{}\n", ii); + } + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + ASSERT_EQ(expected, loopback->get_stdout()); +} + +TEST_F(process_test, shell_stdin_to_stdout) { + std::shared_ptr loopback( + new process_wait(g_io_context, _logger, "/bin/sh")); + + loopback->start_process(); + + std::string expected; + for (unsigned ii = 0; ii < 10; ++ii) { + if (ii > 5) { + // in order to let some async_read_some complete + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + loopback->write_to_stdin(fmt::format("echo hello{}\n", ii)); + expected += fmt::format("hello{}\n", ii); + } + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + ASSERT_EQ(expected, loopback->get_stdout()); +} diff --git a/common/test/test_main.cc b/common/test/test_main.cc index 995392ef7a4..09955d482aa 100644 --- a/common/test/test_main.cc +++ b/common/test/test_main.cc @@ -47,6 +47,7 @@ std::shared_ptr pool_logger = int main(int argc, char* argv[]) { // GTest initialization. testing::InitGoogleTest(&argc, argv); + sigignore(SIGPIPE); // Set specific environment. testing::AddGlobalTestEnvironment(new CentreonEngineEnvironment()); diff --git a/engine/modules/opentelemetry/CMakeLists.txt b/engine/modules/opentelemetry/CMakeLists.txt index bab0f833cd5..7915aeaef67 100644 --- a/engine/modules/opentelemetry/CMakeLists.txt +++ b/engine/modules/opentelemetry/CMakeLists.txt @@ -65,11 +65,6 @@ target_precompile_headers(opentelemetry PRIVATE precomp_inc/precomp.hh) # set(EXTERNALCMD_MODULE "${EXTERNALCMD_MODULE}" PARENT_SCOPE) target_link_libraries(opentelemetry spdlog::spdlog) -add_dependencies(opentelemetry - pb_open_telemetry_lib - pb_neb_lib - pb_tag_lib) - add_dependencies(opentelemetry pb_open_telemetry_lib pb_neb_lib diff --git a/engine/modules/opentelemetry/src/otl_converter.cc b/engine/modules/opentelemetry/src/otl_converter.cc index ecade5c1753..e1ca00e75e1 100644 --- a/engine/modules/opentelemetry/src/otl_converter.cc +++ b/engine/modules/opentelemetry/src/otl_converter.cc @@ -182,4 +182,4 @@ std::shared_ptr otl_converter::create_converter_config( cmd_line, e.what()); throw; } -} \ No newline at end of file +} diff --git a/vcpkg.json b/vcpkg.json index 04388ffde31..5a4e2b5d1d7 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -20,6 +20,7 @@ "boost-multi-index", "boost-interprocess", "boost-exception", + "boost-process", "boost-program-options", "boost-serialization", "boost-url", @@ -27,4 +28,4 @@ "rapidjson", "gtest" ] -} +} \ No newline at end of file