diff --git a/CMakeLists.txt b/CMakeLists.txt index 30116a6c8..9232ccb3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,13 +139,6 @@ set(HAVE_EDITLINE "${EDITLINE_FOUND}") find_package(Termcap) set(HAVE_TERMCAP "${TERMCAP_FOUND}") -find_package(PostgreSQL) - -if(PostgreSQL_FOUND) - link_directories(${PostgreSQL_LIBRARY_DIRS}) - include_directories(${PostgreSQL_INCLUDE_DIRS}) -endif() - include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/lib diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index f37bd7b1e..4da708367 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -39,11 +39,25 @@ if(ICINGA2_WITH_MYSQL OR ICINGA2_WITH_PGSQL) endif() if(ICINGA2_WITH_MYSQL) - add_subdirectory(db_ido_mysql) + find_package(MySQL) + + if(MYSQL_FOUND) + add_subdirectory(db_ido_mysql) + add_subdirectory(mysql_shim) + else() + message(FATAL_ERROR "You have selected MySQL support, but MySQL could not be found. You can disable the MySQL IDO module using -DICINGA2_WITH_MYSQL=OFF.") + endif() endif() if(ICINGA2_WITH_PGSQL) - add_subdirectory(db_ido_pgsql) + find_package(PostgreSQL) + + if(PostgreSQL_FOUND) + add_subdirectory(db_ido_pgsql) + add_subdirectory(pgsql_shim) + else() + message(FATAL_ERROR "You have selected PostgreSQL support, but PostgreSQL could not be found. You can disable the PostgreSQL IDO module using -DICINGA2_WITH_PGSQL=OFF.") + endif() endif() if(ICINGA2_WITH_DEMO) diff --git a/lib/base/CMakeLists.txt b/lib/base/CMakeLists.txt index 2b3d19e84..ba9ee5d0e 100644 --- a/lib/base/CMakeLists.txt +++ b/lib/base/CMakeLists.txt @@ -31,7 +31,7 @@ set(base_SOURCES convert.cpp datetime.cpp datetime.thpp datetime-script.cpp debuginfo.cpp dictionary.cpp dictionary-script.cpp configobject.cpp configobject.thpp configobject-script.cpp configtype.cpp configwriter.cpp dependencygraph.cpp exception.cpp fifo.cpp filelogger.cpp filelogger.thpp initialize.cpp json.cpp - json-script.cpp loader.cpp logger.cpp logger.thpp math-script.cpp + json-script.cpp library.cpp loader.cpp logger.cpp logger.thpp math-script.cpp netstring.cpp networkstream.cpp number.cpp number-script.cpp object.cpp object-script.cpp objecttype.cpp primitivetype.cpp process.cpp ringbuffer.cpp scriptframe.cpp function.cpp function.thpp function-script.cpp diff --git a/lib/base/library.cpp b/lib/base/library.cpp new file mode 100644 index 000000000..c39ca4067 --- /dev/null +++ b/lib/base/library.cpp @@ -0,0 +1,85 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "base/library.hpp" +#include "base/loader.hpp" +#include "base/logger.hpp" +#include "base/exception.hpp" +#include "base/application.hpp" + +using namespace icinga; + +/** + * Loads the specified library. + * + * @param name The name of the library. + */ +Library::Library(const String& name) +{ + String path; +#if defined(_WIN32) + path = name + ".dll"; +#elif defined(__APPLE__) + path = "lib" + name + "." + Application::GetAppSpecVersion() + ".dylib"; +#else /* __APPLE__ */ + path = "lib" + name + ".so." + Application::GetAppSpecVersion(); +#endif /* _WIN32 */ + + Log(LogNotice, "Library") + << "Loading library '" << path << "'"; + +#ifdef _WIN32 + HMODULE hModule = LoadLibrary(path.CStr()); + + if (!hModule) { + BOOST_THROW_EXCEPTION(win32_error() + << boost::errinfo_api_function("LoadLibrary") + << errinfo_win32_error(GetLastError()) + << boost::errinfo_file_name(path)); + } +#else /* _WIN32 */ + void *hModule = dlopen(path.CStr(), RTLD_NOW | RTLD_GLOBAL); + + if (!hModule) { + BOOST_THROW_EXCEPTION(std::runtime_error("Could not load library '" + path + "': " + dlerror())); + } +#endif /* _WIN32 */ + + Loader::ExecuteDeferredInitializers(); + + m_Handle.reset(new LibraryHandle(hModule), [](LibraryHandle *handle) { +#ifdef _WIN32 + FreeLibrary(*handle); +#else /* _WIN32 */ + dlclose(*handle); +#endif /* _WIN32 */ + }); +} + +void *Library::GetSymbolAddress(const String& name) const +{ + if (!m_Handle) + BOOST_THROW_EXCEPTION(std::runtime_error("Invalid library handle")); + +#ifdef _WIN32 + return GetProcAddress(*m_Handle.get(), name.CStr()); +#else /* _WIN32 */ + return dlsym(*m_Handle.get(), name.CStr()); +#endif /* _WIN32 */ +} diff --git a/lib/base/library.hpp b/lib/base/library.hpp new file mode 100644 index 000000000..9ef1fce45 --- /dev/null +++ b/lib/base/library.hpp @@ -0,0 +1,58 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#ifndef LIBRARY_H +#define LIBRARY_H + +#include "base/i2-base.hpp" +#include "base/string.hpp" +#include + +namespace icinga +{ + +#ifndef _WIN32 +typedef void *LibraryHandle; +#else /* _WIN32 */ +typedef HMODULE LibraryHandle; +#endif /* _WIN32 */ + +class Library +{ +public: + Library(void) = default; + Library(const String& name); + + void *GetSymbolAddress(const String& name) const; + + template + T GetSymbolAddress(const String& name) const + { + static_assert(!std::is_same::value, "T must not be void *"); + + return reinterpret_cast(GetSymbolAddress(name)); + } + +private: + std::shared_ptr m_Handle; +}; + +} + +#endif /* LIBRARY_H */ diff --git a/lib/base/loader.cpp b/lib/base/loader.cpp index 43709589e..83ebd879b 100644 --- a/lib/base/loader.cpp +++ b/lib/base/loader.cpp @@ -18,6 +18,9 @@ ******************************************************************************/ #include "base/loader.hpp" +#include "base/logger.hpp" +#include "base/exception.hpp" +#include "base/application.hpp" using namespace icinga; diff --git a/lib/config/expression.cpp b/lib/config/expression.cpp index cc29c1dcd..7b872895c 100644 --- a/lib/config/expression.cpp +++ b/lib/config/expression.cpp @@ -842,7 +842,6 @@ ExpressionResult LibraryExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dh Log(LogNotice, "config") << "Ignoring explicit load request for library \"" << libres << "\"."; - //Loader::LoadExtensionLibrary(libres.GetValue()); return Empty; } diff --git a/lib/db_ido_mysql/CMakeLists.txt b/lib/db_ido_mysql/CMakeLists.txt index d8db6fe2c..5892a9725 100644 --- a/lib/db_ido_mysql/CMakeLists.txt +++ b/lib/db_ido_mysql/CMakeLists.txt @@ -15,47 +15,42 @@ # along with this program; if not, write to the Free Software Foundation # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -find_package(MySQL) +mkclass_target(idomysqlconnection.ti idomysqlconnection.tcpp idomysqlconnection.thpp) -if(MYSQL_FOUND) - mkclass_target(idomysqlconnection.ti idomysqlconnection.tcpp idomysqlconnection.thpp) +set(db_ido_mysql_SOURCES + idomysqlconnection.cpp idomysqlconnection.thpp +) - set(db_ido_mysql_SOURCES - idomysqlconnection.cpp idomysqlconnection.thpp - ) - - if(ICINGA2_UNITY_BUILD) - mkunity_target(db_ido_mysql db_ido_mysql db_ido_mysql_SOURCES) - endif() - - add_library(db_ido_mysql STATIC ${db_ido_mysql_SOURCES}) - - include_directories(${MYSQL_INCLUDE_DIR}) - target_link_libraries(db_ido_mysql ${Boost_LIBRARIES} ${MYSQL_LIB} base config icinga db_ido) - - set_target_properties ( - db_ido_mysql PROPERTIES - FOLDER Components - ) - - install_if_not_exists( - ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/ido-mysql.conf - ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available - ) - - install( - DIRECTORY schema - DESTINATION ${CMAKE_INSTALL_DATADIR}/icinga2-ido-mysql - FILES_MATCHING PATTERN "*.sql" - ) - - install( - DIRECTORY schema/upgrade - DESTINATION ${CMAKE_INSTALL_DATADIR}/icinga2-ido-mysql/schema - FILES_MATCHING PATTERN "*.sql" - ) - - set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE) -else() - message(FATAL_ERROR "You have selected MySQL support, but MySQL could not be found. You can disable the MySQL IDO module using -DICINGA2_WITH_MYSQL=OFF.") +if(ICINGA2_UNITY_BUILD) + mkunity_target(db_ido_mysql db_ido_mysql db_ido_mysql_SOURCES) endif() + +add_library(db_ido_mysql STATIC ${db_ido_mysql_SOURCES}) + +include_directories(${MYSQL_INCLUDE_DIR}) + +target_link_libraries(db_ido_mysql ${Boost_LIBRARIES} base config icinga db_ido) + +set_target_properties ( + db_ido_mysql PROPERTIES + FOLDER Components +) + +install_if_not_exists( + ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/ido-mysql.conf + ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available +) + +install( + DIRECTORY schema + DESTINATION ${CMAKE_INSTALL_DATADIR}/icinga2-ido-mysql + FILES_MATCHING PATTERN "*.sql" +) + +install( + DIRECTORY schema/upgrade + DESTINATION ${CMAKE_INSTALL_DATADIR}/icinga2-ido-mysql/schema + FILES_MATCHING PATTERN "*.sql" +) + +set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE) diff --git a/lib/db_ido_mysql/idomysqlconnection.cpp b/lib/db_ido_mysql/idomysqlconnection.cpp index 972cae880..69e3778b5 100644 --- a/lib/db_ido_mysql/idomysqlconnection.cpp +++ b/lib/db_ido_mysql/idomysqlconnection.cpp @@ -46,6 +46,14 @@ void IdoMysqlConnection::OnConfigLoaded(void) ObjectImpl::OnConfigLoaded(); m_QueryQueue.SetName("IdoMysqlConnection, " + GetName()); + + Library shimLibrary{"mysql_shim"}; + + auto create_mysql_shim = shimLibrary.GetSymbolAddress("create_mysql_shim"); + + m_Mysql.reset(create_mysql_shim()); + + std::swap(m_Library, shimLibrary); } void IdoMysqlConnection::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata) @@ -98,7 +106,7 @@ void IdoMysqlConnection::Resume(void) m_ReconnectTimer->Start(); m_ReconnectTimer->Reschedule(0); - ASSERT(mysql_thread_safe()); + ASSERT(m_Mysql->thread_safe()); } void IdoMysqlConnection::Pause(void) @@ -127,7 +135,7 @@ void IdoMysqlConnection::ExceptionHandler(boost::exception_ptr exp) << "Exception during database operation: " << DiagnosticInformation(exp); if (GetConnected()) { - mysql_close(&m_Connection); + m_Mysql->close(&m_Connection); SetConnected(false); } @@ -146,7 +154,7 @@ void IdoMysqlConnection::Disconnect(void) return; Query("COMMIT"); - mysql_close(&m_Connection); + m_Mysql->close(&m_Connection); SetConnected(false); } @@ -205,10 +213,10 @@ void IdoMysqlConnection::Reconnect(void) if (GetConnected()) { /* Check if we're really still connected */ - if (mysql_ping(&m_Connection) == 0) + if (m_Mysql->ping(&m_Connection) == 0) return; - mysql_close(&m_Connection); + m_Mysql->close(&m_Connection); SetConnected(false); reconnect = true; } @@ -249,7 +257,7 @@ void IdoMysqlConnection::Reconnect(void) sslCipher = (!isslCipher.IsEmpty()) ? isslCipher.CStr() : nullptr; /* connection */ - if (!mysql_init(&m_Connection)) { + if (!m_Mysql->init(&m_Connection)) { Log(LogCritical, "IdoMysqlConnection") << "mysql_init() failed: out of memory"; @@ -257,14 +265,14 @@ void IdoMysqlConnection::Reconnect(void) } if (enableSsl) - mysql_ssl_set(&m_Connection, sslKey, sslCert, sslCa, sslCaPath, sslCipher); + m_Mysql->ssl_set(&m_Connection, sslKey, sslCert, sslCa, sslCaPath, sslCipher); - if (!mysql_real_connect(&m_Connection, host, user, passwd, db, port, socket_path, CLIENT_FOUND_ROWS | CLIENT_MULTI_STATEMENTS)) { + if (!m_Mysql->real_connect(&m_Connection, host, user, passwd, db, port, socket_path, CLIENT_FOUND_ROWS | CLIENT_MULTI_STATEMENTS)) { Log(LogCritical, "IdoMysqlConnection") << "Connection to database '" << db << "' with user '" << user << "' on '" << host << ":" << port - << "' " << (enableSsl ? "(SSL enabled) " : "") << "failed: \"" << mysql_error(&m_Connection) << "\""; + << "' " << (enableSsl ? "(SSL enabled) " : "") << "failed: \"" << m_Mysql->error(&m_Connection) << "\""; - BOOST_THROW_EXCEPTION(std::runtime_error(mysql_error(&m_Connection))); + BOOST_THROW_EXCEPTION(std::runtime_error(m_Mysql->error(&m_Connection))); } SetConnected(true); @@ -286,7 +294,7 @@ void IdoMysqlConnection::Reconnect(void) row = FetchRow(result); if (!row) { - mysql_close(&m_Connection); + m_Mysql->close(&m_Connection); SetConnected(false); Log(LogCritical, "IdoMysqlConnection", "Schema does not provide any valid version! Verify your schema installation."); @@ -301,7 +309,7 @@ void IdoMysqlConnection::Reconnect(void) SetSchemaVersion(version); if (Utility::CompareVersion(IDO_COMPAT_SCHEMA_VERSION, version) < 0) { - mysql_close(&m_Connection); + m_Mysql->close(&m_Connection); SetConnected(false); Log(LogCritical, "IdoMysqlConnection") @@ -358,7 +366,7 @@ void IdoMysqlConnection::Reconnect(void) << "Last update by '" << endpoint_name << "' was " << status_update_age << "s ago."; if (status_update_age < GetFailoverTimeout()) { - mysql_close(&m_Connection); + m_Mysql->close(&m_Connection); SetConnected(false); SetShouldConnect(false); @@ -370,7 +378,7 @@ void IdoMysqlConnection::Reconnect(void) Log(LogNotice, "IdoMysqlConnection") << "Local endpoint '" << my_endpoint->GetName() << "' is not authoritative, bailing out."; - mysql_close(&m_Connection); + m_Mysql->close(&m_Connection); SetConnected(false); return; @@ -533,15 +541,15 @@ void IdoMysqlConnection::FinishAsyncQueries(void) String query = querybuf.str(); - if (mysql_query(&m_Connection, query.CStr()) != 0) { + if (m_Mysql->query(&m_Connection, query.CStr()) != 0) { std::ostringstream msgbuf; - String message = mysql_error(&m_Connection); + String message = m_Mysql->error(&m_Connection); msgbuf << "Error \"" << message << "\" when executing query \"" << query << "\""; Log(LogCritical, "IdoMysqlConnection", msgbuf.str()); BOOST_THROW_EXCEPTION( database_error() - << errinfo_message(mysql_error(&m_Connection)) + << errinfo_message(m_Mysql->error(&m_Connection)) << errinfo_database_query(query) ); } @@ -549,40 +557,40 @@ void IdoMysqlConnection::FinishAsyncQueries(void) for (std::vector::size_type i = offset; i < offset + count; i++) { const IdoAsyncQuery& aq = queries[i]; - MYSQL_RES *result = mysql_store_result(&m_Connection); + MYSQL_RES *result = m_Mysql->store_result(&m_Connection); - m_AffectedRows = mysql_affected_rows(&m_Connection); + m_AffectedRows = m_Mysql->affected_rows(&m_Connection); IdoMysqlResult iresult; if (!result) { - if (mysql_field_count(&m_Connection) > 0) { + if (m_Mysql->field_count(&m_Connection) > 0) { std::ostringstream msgbuf; - String message = mysql_error(&m_Connection); + String message = m_Mysql->error(&m_Connection); msgbuf << "Error \"" << message << "\" when executing query \"" << aq.Query << "\""; Log(LogCritical, "IdoMysqlConnection", msgbuf.str()); BOOST_THROW_EXCEPTION( database_error() - << errinfo_message(mysql_error(&m_Connection)) + << errinfo_message(m_Mysql->error(&m_Connection)) << errinfo_database_query(query) ); } } else - iresult = IdoMysqlResult(result, std::ptr_fun(mysql_free_result)); + iresult = IdoMysqlResult(result, std::bind(&MysqlInterface::free_result, std::cref(m_Mysql), _1)); if (aq.Callback) aq.Callback(iresult); - if (mysql_next_result(&m_Connection) > 0) { + if (m_Mysql->next_result(&m_Connection) > 0) { std::ostringstream msgbuf; - String message = mysql_error(&m_Connection); + String message = m_Mysql->error(&m_Connection); msgbuf << "Error \"" << message << "\" when executing query \"" << query << "\""; Log(LogCritical, "IdoMysqlConnection", msgbuf.str()); BOOST_THROW_EXCEPTION( database_error() - << errinfo_message(mysql_error(&m_Connection)) + << errinfo_message(m_Mysql->error(&m_Connection)) << errinfo_database_query(query) ); } @@ -604,33 +612,33 @@ IdoMysqlResult IdoMysqlConnection::Query(const String& query) IncreaseQueryCount(); - if (mysql_query(&m_Connection, query.CStr()) != 0) { + if (m_Mysql->query(&m_Connection, query.CStr()) != 0) { std::ostringstream msgbuf; - String message = mysql_error(&m_Connection); + String message = m_Mysql->error(&m_Connection); msgbuf << "Error \"" << message << "\" when executing query \"" << query << "\""; Log(LogCritical, "IdoMysqlConnection", msgbuf.str()); BOOST_THROW_EXCEPTION( database_error() - << errinfo_message(mysql_error(&m_Connection)) + << errinfo_message(m_Mysql->error(&m_Connection)) << errinfo_database_query(query) ); } - MYSQL_RES *result = mysql_store_result(&m_Connection); + MYSQL_RES *result = m_Mysql->store_result(&m_Connection); - m_AffectedRows = mysql_affected_rows(&m_Connection); + m_AffectedRows = m_Mysql->affected_rows(&m_Connection); if (!result) { - if (mysql_field_count(&m_Connection) > 0) { + if (m_Mysql->field_count(&m_Connection) > 0) { std::ostringstream msgbuf; - String message = mysql_error(&m_Connection); + String message = m_Mysql->error(&m_Connection); msgbuf << "Error \"" << message << "\" when executing query \"" << query << "\""; Log(LogCritical, "IdoMysqlConnection", msgbuf.str()); BOOST_THROW_EXCEPTION( database_error() - << errinfo_message(mysql_error(&m_Connection)) + << errinfo_message(m_Mysql->error(&m_Connection)) << errinfo_database_query(query) ); } @@ -638,14 +646,14 @@ IdoMysqlResult IdoMysqlConnection::Query(const String& query) return IdoMysqlResult(); } - return IdoMysqlResult(result, std::ptr_fun(mysql_free_result)); + return IdoMysqlResult(result, std::bind(&MysqlInterface::free_result, std::cref(m_Mysql), _1)); } DbReference IdoMysqlConnection::GetLastInsertID(void) { AssertOnWorkQueue(); - return DbReference(mysql_insert_id(&m_Connection)); + return DbReference(m_Mysql->insert_id(&m_Connection)); } int IdoMysqlConnection::GetAffectedRows(void) @@ -664,7 +672,7 @@ String IdoMysqlConnection::Escape(const String& s) size_t length = utf8s.GetLength(); char *to = new char[utf8s.GetLength() * 2 + 1]; - mysql_real_escape_string(&m_Connection, to, utf8s.CStr(), length); + m_Mysql->real_escape_string(&m_Connection, to, utf8s.CStr(), length); String result = String(to); @@ -681,20 +689,20 @@ Dictionary::Ptr IdoMysqlConnection::FetchRow(const IdoMysqlResult& result) MYSQL_FIELD *field; unsigned long *lengths, i; - row = mysql_fetch_row(result.get()); + row = m_Mysql->fetch_row(result.get()); if (!row) return nullptr; - lengths = mysql_fetch_lengths(result.get()); + lengths = m_Mysql->fetch_lengths(result.get()); if (!lengths) return nullptr; Dictionary::Ptr dict = new Dictionary(); - mysql_field_seek(result.get(), 0); - for (field = mysql_fetch_field(result.get()), i = 0; field; field = mysql_fetch_field(result.get()), i++) + m_Mysql->field_seek(result.get(), 0); + for (field = m_Mysql->fetch_field(result.get()), i = 0; field; field = m_Mysql->fetch_field(result.get()), i++) dict->Set(field->name, String(row[i], row[i] + lengths[i])); return dict; diff --git a/lib/db_ido_mysql/idomysqlconnection.hpp b/lib/db_ido_mysql/idomysqlconnection.hpp index 94a5120c8..2b0ce81bb 100644 --- a/lib/db_ido_mysql/idomysqlconnection.hpp +++ b/lib/db_ido_mysql/idomysqlconnection.hpp @@ -21,10 +21,11 @@ #define IDOMYSQLCONNECTION_H #include "db_ido_mysql/idomysqlconnection.thpp" +#include "mysql_shim/mysqlinterface.hpp" #include "base/array.hpp" #include "base/timer.hpp" #include "base/workqueue.hpp" -#include +#include "base/library.hpp" namespace icinga { @@ -74,6 +75,9 @@ private: WorkQueue m_QueryQueue; + Library m_Library; + std::unique_ptr m_Mysql; + MYSQL m_Connection; int m_AffectedRows; unsigned int m_MaxPacketSize; diff --git a/lib/db_ido_pgsql/CMakeLists.txt b/lib/db_ido_pgsql/CMakeLists.txt index d1eb092d6..a1b5e0913 100644 --- a/lib/db_ido_pgsql/CMakeLists.txt +++ b/lib/db_ido_pgsql/CMakeLists.txt @@ -15,44 +15,42 @@ # along with this program; if not, write to the Free Software Foundation # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -if(PostgreSQL_FOUND) - mkclass_target(idopgsqlconnection.ti idopgsqlconnection.tcpp idopgsqlconnection.thpp) +mkclass_target(idopgsqlconnection.ti idopgsqlconnection.tcpp idopgsqlconnection.thpp) - set(db_ido_pgsql_SOURCES - idopgsqlconnection.cpp idopgsqlconnection.thpp - ) +set(db_ido_pgsql_SOURCES + idopgsqlconnection.cpp idopgsqlconnection.thpp +) - if(ICINGA2_UNITY_BUILD) - mkunity_target(db_ido_pgsql db_ido_pgsql db_ido_pgsql_SOURCES) - endif() - - add_library(db_ido_pgsql STATIC ${db_ido_pgsql_SOURCES}) - - target_link_libraries(db_ido_pgsql ${Boost_LIBRARIES} ${PostgreSQL_LIBRARIES} base config icinga db_ido) - - set_target_properties ( - db_ido_pgsql PROPERTIES - FOLDER Components - ) - - install_if_not_exists( - ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/ido-pgsql.conf - ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available - ) - - install( - DIRECTORY schema - DESTINATION ${CMAKE_INSTALL_DATADIR}/icinga2-ido-pgsql - FILES_MATCHING PATTERN "*.sql" - ) - - install( - DIRECTORY schema/upgrade - DESTINATION ${CMAKE_INSTALL_DATADIR}/icinga2-ido-pgsql/schema - FILES_MATCHING PATTERN "*.sql" - ) - - set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE) -else() - message(FATAL_ERROR "You have selected PostgreSQL support, but PostgreSQL could not be found. You can disable the PostgreSQL IDO module using -DICINGA2_WITH_PGSQL=OFF.") +if(ICINGA2_UNITY_BUILD) + mkunity_target(db_ido_pgsql db_ido_pgsql db_ido_pgsql_SOURCES) endif() + +add_library(db_ido_pgsql STATIC ${db_ido_pgsql_SOURCES}) + +include_directories(${PostgreSQL_INCLUDE_DIRS}) + +target_link_libraries(db_ido_pgsql ${Boost_LIBRARIES} base config icinga db_ido) + +set_target_properties ( + db_ido_pgsql PROPERTIES + FOLDER Components +) + +install_if_not_exists( + ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/ido-pgsql.conf + ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available +) + +install( + DIRECTORY schema + DESTINATION ${CMAKE_INSTALL_DATADIR}/icinga2-ido-pgsql + FILES_MATCHING PATTERN "*.sql" +) + +install( + DIRECTORY schema/upgrade + DESTINATION ${CMAKE_INSTALL_DATADIR}/icinga2-ido-pgsql/schema + FILES_MATCHING PATTERN "*.sql" +) + +set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE) diff --git a/lib/db_ido_pgsql/idopgsqlconnection.cpp b/lib/db_ido_pgsql/idopgsqlconnection.cpp index 3f44afa46..7ed0d7170 100644 --- a/lib/db_ido_pgsql/idopgsqlconnection.cpp +++ b/lib/db_ido_pgsql/idopgsqlconnection.cpp @@ -50,6 +50,14 @@ void IdoPgsqlConnection::OnConfigLoaded(void) ObjectImpl::OnConfigLoaded(); m_QueryQueue.SetName("IdoPgsqlConnection, " + GetName()); + + Library shimLibrary{"pgsql_shim"}; + + auto create_pgsql_shim = shimLibrary.GetSymbolAddress("create_pgsql_shim"); + + m_Pgsql.reset(create_pgsql_shim()); + + std::swap(m_Library, shimLibrary); } void IdoPgsqlConnection::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata) @@ -102,7 +110,7 @@ void IdoPgsqlConnection::Resume(void) m_ReconnectTimer->Start(); m_ReconnectTimer->Reschedule(0); - ASSERT(PQisthreadsafe()); + ASSERT(m_Pgsql->isthreadsafe()); } void IdoPgsqlConnection::Pause(void) @@ -126,7 +134,7 @@ void IdoPgsqlConnection::ExceptionHandler(boost::exception_ptr exp) << "Exception during database operation: " << DiagnosticInformation(exp); if (GetConnected()) { - PQfinish(m_Connection); + m_Pgsql->finish(m_Connection); SetConnected(false); } } @@ -145,7 +153,7 @@ void IdoPgsqlConnection::Disconnect(void) Query("COMMIT"); - PQfinish(m_Connection); + m_Pgsql->finish(m_Connection); SetConnected(false); } @@ -193,7 +201,7 @@ void IdoPgsqlConnection::Reconnect(void) Query("SELECT 1"); return; } catch (const std::exception&) { - PQfinish(m_Connection); + m_Pgsql->finish(m_Connection); SetConnected(false); reconnect = true; } @@ -216,14 +224,14 @@ void IdoPgsqlConnection::Reconnect(void) passwd = (!ipasswd.IsEmpty()) ? ipasswd.CStr() : nullptr; db = (!idb.IsEmpty()) ? idb.CStr() : nullptr; - m_Connection = PQsetdbLogin(host, port, nullptr, nullptr, db, user, passwd); + m_Connection = m_Pgsql->setdbLogin(host, port, nullptr, nullptr, db, user, passwd); if (!m_Connection) return; - if (PQstatus(m_Connection) != CONNECTION_OK) { - String message = PQerrorMessage(m_Connection); - PQfinish(m_Connection); + if (m_Pgsql->status(m_Connection) != CONNECTION_OK) { + String message = m_Pgsql->errorMessage(m_Connection); + m_Pgsql->finish(m_Connection); SetConnected(false); Log(LogCritical, "IdoPgsqlConnection") @@ -240,7 +248,7 @@ void IdoPgsqlConnection::Reconnect(void) /* explicitely require legacy mode for string escaping in PostgreSQL >= 9.1 * changing standard_conforming_strings to on by default */ - if (PQserverVersion(m_Connection) >= 90100) + if (m_Pgsql->serverVersion(m_Connection) >= 90100) result = Query("SET standard_conforming_strings TO off"); String dbVersionName = "idoutils"; @@ -249,7 +257,7 @@ void IdoPgsqlConnection::Reconnect(void) Dictionary::Ptr row = FetchRow(result, 0); if (!row) { - PQfinish(m_Connection); + m_Pgsql->finish(m_Connection); SetConnected(false); Log(LogCritical, "IdoPgsqlConnection", "Schema does not provide any valid version! Verify your schema installation."); @@ -262,7 +270,7 @@ void IdoPgsqlConnection::Reconnect(void) SetSchemaVersion(version); if (Utility::CompareVersion(IDO_COMPAT_SCHEMA_VERSION, version) < 0) { - PQfinish(m_Connection); + m_Pgsql->finish(m_Connection); SetConnected(false); Log(LogCritical, "IdoPgsqlConnection") @@ -316,7 +324,7 @@ void IdoPgsqlConnection::Reconnect(void) << "Last update by '" << endpoint_name << "' was " << status_update_age << "s ago."; if (status_update_age < GetFailoverTimeout()) { - PQfinish(m_Connection); + m_Pgsql->finish(m_Connection); SetConnected(false); SetShouldConnect(false); @@ -328,7 +336,7 @@ void IdoPgsqlConnection::Reconnect(void) Log(LogNotice, "IdoPgsqlConnection") << "Local endpoint '" << my_endpoint->GetName() << "' is not authoritative, bailing out."; - PQfinish(m_Connection); + m_Pgsql->finish(m_Connection); SetConnected(false); return; @@ -437,10 +445,10 @@ IdoPgsqlResult IdoPgsqlConnection::Query(const String& query) IncreaseQueryCount(); - PGresult *result = PQexec(m_Connection, query.CStr()); + PGresult *result = m_Pgsql->exec(m_Connection, query.CStr()); if (!result) { - String message = PQerrorMessage(m_Connection); + String message = m_Pgsql->errorMessage(m_Connection); Log(LogCritical, "IdoPgsqlConnection") << "Error \"" << message << "\" when executing query \"" << query << "\""; @@ -451,17 +459,17 @@ IdoPgsqlResult IdoPgsqlConnection::Query(const String& query) ); } - char *rowCount = PQcmdTuples(result); + char *rowCount = m_Pgsql->cmdTuples(result); m_AffectedRows = atoi(rowCount); - if (PQresultStatus(result) == PGRES_COMMAND_OK) { - PQclear(result); + if (m_Pgsql->resultStatus(result) == PGRES_COMMAND_OK) { + m_Pgsql->clear(result); return IdoPgsqlResult(); } - if (PQresultStatus(result) != PGRES_TUPLES_OK) { - String message = PQresultErrorMessage(result); - PQclear(result); + if (m_Pgsql->resultStatus(result) != PGRES_TUPLES_OK) { + String message = m_Pgsql->resultErrorMessage(result); + m_Pgsql->clear(result); Log(LogCritical, "IdoPgsqlConnection") << "Error \"" << message << "\" when executing query \"" << query << "\""; @@ -473,7 +481,7 @@ IdoPgsqlResult IdoPgsqlConnection::Query(const String& query) ); } - return IdoPgsqlResult(result, std::ptr_fun(PQclear)); + return IdoPgsqlResult(result, std::bind(&PgsqlInterface::clear, std::cref(m_Pgsql), _1)); } DbReference IdoPgsqlConnection::GetSequenceValue(const String& table, const String& column) @@ -508,7 +516,7 @@ String IdoPgsqlConnection::Escape(const String& s) size_t length = utf8s.GetLength(); char *to = new char[utf8s.GetLength() * 2 + 1]; - PQescapeStringConn(m_Connection, to, utf8s.CStr(), length, nullptr); + m_Pgsql->escapeStringConn(m_Connection, to, utf8s.CStr(), length, nullptr); String result = String(to); @@ -521,20 +529,20 @@ Dictionary::Ptr IdoPgsqlConnection::FetchRow(const IdoPgsqlResult& result, int r { AssertOnWorkQueue(); - if (row >= PQntuples(result.get())) + if (row >= m_Pgsql->ntuples(result.get())) return nullptr; - int columns = PQnfields(result.get()); + int columns = m_Pgsql->nfields(result.get()); Dictionary::Ptr dict = new Dictionary(); for (int column = 0; column < columns; column++) { Value value; - if (!PQgetisnull(result.get(), row, column)) - value = PQgetvalue(result.get(), row, column); + if (!m_Pgsql->getisnull(result.get(), row, column)) + value = m_Pgsql->getvalue(result.get(), row, column); - dict->Set(PQfname(result.get(), column), value); + dict->Set(m_Pgsql->fname(result.get(), column), value); } return dict; diff --git a/lib/db_ido_pgsql/idopgsqlconnection.hpp b/lib/db_ido_pgsql/idopgsqlconnection.hpp index 9bad5af43..577bdf348 100644 --- a/lib/db_ido_pgsql/idopgsqlconnection.hpp +++ b/lib/db_ido_pgsql/idopgsqlconnection.hpp @@ -21,10 +21,11 @@ #define IDOPGSQLCONNECTION_H #include "db_ido_pgsql/idopgsqlconnection.thpp" +#include "pgsql_shim/pgsqlinterface.hpp" #include "base/array.hpp" #include "base/timer.hpp" #include "base/workqueue.hpp" -#include +#include "base/library.hpp" namespace icinga { @@ -66,6 +67,9 @@ private: WorkQueue m_QueryQueue; + Library m_Library; + std::unique_ptr m_Pgsql; + PGconn *m_Connection; int m_AffectedRows; diff --git a/lib/mysql_shim/CMakeLists.txt b/lib/mysql_shim/CMakeLists.txt new file mode 100644 index 000000000..ceff89802 --- /dev/null +++ b/lib/mysql_shim/CMakeLists.txt @@ -0,0 +1,46 @@ +# Icinga 2 +# Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +include_directories(${MYSQL_INCLUDE_DIR}) + +set(mysql_shim_SOURCES + mysql_shim.def + mysqlinterface.cpp +) + +if(ICINGA2_UNITY_BUILD) + mkunity_target(mysql_shim mysql_shim mysql_shim_SOURCES) +endif() + +add_library(mysql_shim SHARED ${mysql_shim_SOURCES}) + +include(GenerateExportHeader) +generate_export_header(mysql_shim) + +target_link_libraries(mysql_shim ${MYSQL_LIB}) + +set_target_properties ( + mysql_shim PROPERTIES + FOLDER Lib + VERSION ${SPEC_VERSION} +) + +install( + TARGETS mysql_shim + RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/icinga2 +) diff --git a/lib/mysql_shim/mysql_shim.def b/lib/mysql_shim/mysql_shim.def new file mode 100644 index 000000000..ae3676500 --- /dev/null +++ b/lib/mysql_shim/mysql_shim.def @@ -0,0 +1,3 @@ +LIBRARY mysql_shim +EXPORTS + create_mysql_shim diff --git a/lib/mysql_shim/mysqlinterface.cpp b/lib/mysql_shim/mysqlinterface.cpp new file mode 100644 index 000000000..bbc9abea8 --- /dev/null +++ b/lib/mysql_shim/mysqlinterface.cpp @@ -0,0 +1,131 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "mysql_shim/mysqlinterface.hpp" + +using namespace icinga; + +struct MysqlInterfaceImpl : public MysqlInterface +{ + void Destroy(void) override + { + delete this; + } + + my_ulonglong affected_rows(MYSQL *mysql) const override + { + return mysql_affected_rows(mysql); + } + + void close(MYSQL *sock) const override + { + return mysql_close(sock); + } + + const char *error(MYSQL *mysql) const override + { + return mysql_error(mysql); + } + + MYSQL_FIELD *fetch_field(MYSQL_RES *result) const override + { + return mysql_fetch_field(result); + } + + unsigned long *fetch_lengths(MYSQL_RES *result) const override + { + return mysql_fetch_lengths(result); + } + + MYSQL_ROW fetch_row(MYSQL_RES *result) const override + { + return mysql_fetch_row(result); + } + + unsigned int field_count(MYSQL *mysql) const override + { + return mysql_field_count(mysql); + } + + MYSQL_FIELD_OFFSET field_seek(MYSQL_RES *result, MYSQL_FIELD_OFFSET offset) const override + { + return mysql_field_seek(result, offset); + } + + void free_result(MYSQL_RES *result) const override + { + mysql_free_result(result); + } + + MYSQL *init(MYSQL *mysql) const override + { + return mysql_init(mysql); + } + + my_ulonglong insert_id(MYSQL *mysql) const override + { + return mysql_insert_id(mysql); + } + + int next_result(MYSQL *mysql) const override + { + return mysql_next_result(mysql); + } + + int ping(MYSQL *mysql) const override + { + return mysql_ping(mysql); + } + + int query(MYSQL *mysql, const char *q) const override + { + return mysql_query(mysql, q); + } + + MYSQL *real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, + const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag) const override + { + return mysql_real_connect(mysql, host, user, passwd, db, port, unix_socket, clientflag); + } + + unsigned long real_escape_string(MYSQL *mysql, char *to, const char *from, unsigned long length) const override + { + return mysql_real_escape_string(mysql, to, from, length); + } + + my_bool ssl_set(MYSQL *mysql, const char *key, const char *cert, const char *ca, const char *capath, const char *cipher) const override + { + return mysql_ssl_set(mysql, key, cert, ca, capath, cipher); + } + + MYSQL_RES *store_result(MYSQL *mysql) const override + { + return mysql_store_result(mysql); + } + + unsigned int thread_safe(void) const override + { + return mysql_thread_safe(); + } +}; + +MysqlInterface *create_mysql_shim(void) +{ + return new MysqlInterfaceImpl(); +} diff --git a/lib/mysql_shim/mysqlinterface.hpp b/lib/mysql_shim/mysqlinterface.hpp new file mode 100644 index 000000000..77b4039d9 --- /dev/null +++ b/lib/mysql_shim/mysqlinterface.hpp @@ -0,0 +1,81 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#ifndef MYSQLINTERFACE_H +#define MYSQLINTERFACE_H + +#include "mysql_shim/mysql_shim_export.h" +#include +#include + +namespace icinga +{ + +struct MysqlInterface +{ + MysqlInterface(const MysqlInterface&) = delete; + MysqlInterface& operator=(MysqlInterface&) = delete; + + virtual void Destroy(void) = 0; + + virtual my_ulonglong affected_rows(MYSQL *mysql) const = 0; + virtual void close(MYSQL *sock) const = 0; + virtual const char *error(MYSQL *mysql) const = 0; + virtual MYSQL_FIELD *fetch_field(MYSQL_RES *result) const = 0; + virtual unsigned long *fetch_lengths(MYSQL_RES *result) const = 0; + virtual MYSQL_ROW fetch_row(MYSQL_RES *result) const = 0; + virtual unsigned int field_count(MYSQL *mysql) const = 0; + virtual MYSQL_FIELD_OFFSET field_seek(MYSQL_RES *result, + MYSQL_FIELD_OFFSET offset) const = 0; + virtual void free_result(MYSQL_RES *result) const = 0; + virtual MYSQL *init(MYSQL *mysql) const = 0; + virtual my_ulonglong insert_id(MYSQL *mysql) const = 0; + virtual int next_result(MYSQL *mysql) const = 0; + virtual int ping(MYSQL *mysql) const = 0; + virtual int query(MYSQL *mysql, const char *q) const = 0; + virtual MYSQL *real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, + const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag) const = 0; + virtual unsigned long real_escape_string(MYSQL *mysql, char *to, const char *from, unsigned long length) const = 0; + virtual my_bool ssl_set(MYSQL *mysql, const char *key, const char *cert, const char *ca, const char *capath, const char *cipher) const = 0; + virtual MYSQL_RES *store_result(MYSQL *mysql) const = 0; + virtual unsigned int thread_safe(void) const = 0; + +protected: + MysqlInterface(void) = default; + ~MysqlInterface(void) = default; +}; + +struct MysqlInterfaceDeleter +{ + void operator()(MysqlInterface *ifc) const + { + ifc->Destroy(); + } +}; + +} + +extern "C" +{ + MYSQL_SHIM_EXPORT icinga::MysqlInterface *create_mysql_shim(void); +} + +typedef icinga::MysqlInterface *(*create_mysql_shim_ptr)(void); + +#endif /* MYSQLINTERFACE_H */ diff --git a/lib/pgsql_shim/CMakeLists.txt b/lib/pgsql_shim/CMakeLists.txt new file mode 100644 index 000000000..20f09dce3 --- /dev/null +++ b/lib/pgsql_shim/CMakeLists.txt @@ -0,0 +1,47 @@ +# Icinga 2 +# Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +link_directories(${PostgreSQL_LIBRARY_DIRS}) +include_directories(${PostgreSQL_INCLUDE_DIRS}) + +set(pgsql_shim_SOURCES + pgsql_shim.def + pgsqlinterface.cpp +) + +if(ICINGA2_UNITY_BUILD) + mkunity_target(pgsql_shim pgsql_shim pgsql_shim_SOURCES) +endif() + +add_library(pgsql_shim SHARED ${pgsql_shim_SOURCES}) + +include(GenerateExportHeader) +generate_export_header(pgsql_shim) + +target_link_libraries(pgsql_shim ${PostgreSQL_LIBRARIES}) + +set_target_properties ( + pgsql_shim PROPERTIES + FOLDER Lib + VERSION ${SPEC_VERSION} +) + +install( + TARGETS pgsql_shim + RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/icinga2 +) diff --git a/lib/pgsql_shim/pgsql_shim.def b/lib/pgsql_shim/pgsql_shim.def new file mode 100644 index 000000000..7580d6716 --- /dev/null +++ b/lib/pgsql_shim/pgsql_shim.def @@ -0,0 +1,3 @@ +LIBRARY pgsql_shim +EXPORTS + create_pgsql_shim diff --git a/lib/pgsql_shim/pgsqlinterface.cpp b/lib/pgsql_shim/pgsqlinterface.cpp new file mode 100644 index 000000000..fef41f475 --- /dev/null +++ b/lib/pgsql_shim/pgsqlinterface.cpp @@ -0,0 +1,120 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "pgsql_shim/pgsqlinterface.hpp" + +using namespace icinga; + +struct PgsqlInterfaceImpl : public PgsqlInterface +{ + void Destroy(void) override + { + delete this; + } + + void clear(PGresult *res) const override + { + PQclear(res); + } + + char *cmdTuples(PGresult *res) const override + { + return PQcmdTuples(res); + } + + char *errorMessage(const PGconn *conn) const override + { + return PQerrorMessage(conn); + } + + size_t escapeStringConn(PGconn *conn, char *to, const char *from, size_t length, int *error) const override + { + return PQescapeStringConn(conn, to, from, length, error); + } + + PGresult *exec(PGconn *conn, const char *query) const override + { + return PQexec(conn, query); + } + + void finish(PGconn *conn) const override + { + PQfinish(conn); + } + + char *fname(const PGresult *res, int field_num) const override + { + return PQfname(res, field_num); + } + + int getisnull(const PGresult *res, int tup_num, int field_num) const override + { + return PQgetisnull(res, tup_num, field_num); + } + + char *getvalue(const PGresult *res, int tup_num, int field_num) const override + { + return PQgetvalue(res, tup_num, field_num); + } + + int isthreadsafe(void) const override + { + return PQisthreadsafe(); + } + + int nfields(const PGresult *res) const override + { + return PQnfields(res); + } + + int ntuples(const PGresult *res) const override + { + return PQntuples(res); + } + + char *resultErrorMessage(const PGresult *res) const override + { + return PQresultErrorMessage(res); + } + + ExecStatusType resultStatus(const PGresult *res) const override + { + return PQresultStatus(res); + } + + int serverVersion(const PGconn *conn) const override + { + return PQserverVersion(conn); + } + + PGconn *setdbLogin(const char *pghost, const char *pgport, const char *pgoptions, const char *pgtty, const char *dbName, const char *login, const char *pwd) const override + { + return PQsetdbLogin(pghost, pgport, pgoptions, pgtty, dbName, login, pwd); + } + + ConnStatusType status(const PGconn *conn) const override + { + return PQstatus(conn); + } +}; + +PgsqlInterface *create_pgsql_shim(void) +{ + return new PgsqlInterfaceImpl(); +} diff --git a/lib/pgsql_shim/pgsqlinterface.hpp b/lib/pgsql_shim/pgsqlinterface.hpp new file mode 100644 index 000000000..58a733a73 --- /dev/null +++ b/lib/pgsql_shim/pgsqlinterface.hpp @@ -0,0 +1,77 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#ifndef PGSQLINTERFACE_H +#define PGSQLINTERFACE_H + +#include "pgsql_shim/pgsql_shim_export.h" +#include +#include + +namespace icinga +{ + +struct PgsqlInterface +{ + PgsqlInterface(const PgsqlInterface&) = delete; + PgsqlInterface& operator=(PgsqlInterface&) = delete; + + virtual void Destroy(void) = 0; + + virtual void clear(PGresult *res) const = 0; + virtual char *cmdTuples(PGresult *res) const = 0; + virtual char *errorMessage(const PGconn *conn) const = 0; + virtual size_t escapeStringConn(PGconn *conn, char *to, const char *from, size_t length, int *error) const = 0; + virtual PGresult *exec(PGconn *conn, const char *query) const = 0; + virtual void finish(PGconn *conn) const = 0; + virtual char *fname(const PGresult *res, int field_num) const = 0; + virtual int getisnull(const PGresult *res, int tup_num, int field_num) const = 0; + virtual char *getvalue(const PGresult *res, int tup_num, int field_num) const = 0; + virtual int isthreadsafe(void) const = 0; + virtual int nfields(const PGresult *res) const = 0; + virtual int ntuples(const PGresult *res) const = 0; + virtual char *resultErrorMessage(const PGresult *res) const = 0; + virtual ExecStatusType resultStatus(const PGresult *res) const = 0; + virtual int serverVersion(const PGconn *conn) const = 0; + virtual PGconn *setdbLogin(const char *pghost, const char *pgport, const char *pgoptions, const char *pgtty, const char *dbName, const char *login, const char *pwd) const = 0; + virtual ConnStatusType status(const PGconn *conn) const = 0; + +protected: + PgsqlInterface(void) = default; + ~PgsqlInterface(void) = default; +}; + +struct PgsqlInterfaceDeleter +{ + void operator()(PgsqlInterface *ifc) const + { + ifc->Destroy(); + } +}; + +} + +extern "C" +{ + PGSQL_SHIM_EXPORT icinga::PgsqlInterface *create_pgsql_shim(void); +} + +typedef icinga::PgsqlInterface *(*create_pgsql_shim_ptr)(void); + +#endif /* PGSQLINTERFACE_H */