]> granicus.if.org Git - icinga2/commitdiff
Implement shim for libmysqlclient and libpq 5927/head
authorGunnar Beutner <gunnar.beutner@icinga.com>
Mon, 1 Jan 2018 14:21:14 +0000 (15:21 +0100)
committerGunnar Beutner <gunnar.beutner@icinga.com>
Tue, 2 Jan 2018 22:29:48 +0000 (23:29 +0100)
21 files changed:
CMakeLists.txt
lib/CMakeLists.txt
lib/base/CMakeLists.txt
lib/base/library.cpp [new file with mode: 0644]
lib/base/library.hpp [new file with mode: 0644]
lib/base/loader.cpp
lib/config/expression.cpp
lib/db_ido_mysql/CMakeLists.txt
lib/db_ido_mysql/idomysqlconnection.cpp
lib/db_ido_mysql/idomysqlconnection.hpp
lib/db_ido_pgsql/CMakeLists.txt
lib/db_ido_pgsql/idopgsqlconnection.cpp
lib/db_ido_pgsql/idopgsqlconnection.hpp
lib/mysql_shim/CMakeLists.txt [new file with mode: 0644]
lib/mysql_shim/mysql_shim.def [new file with mode: 0644]
lib/mysql_shim/mysqlinterface.cpp [new file with mode: 0644]
lib/mysql_shim/mysqlinterface.hpp [new file with mode: 0644]
lib/pgsql_shim/CMakeLists.txt [new file with mode: 0644]
lib/pgsql_shim/pgsql_shim.def [new file with mode: 0644]
lib/pgsql_shim/pgsqlinterface.cpp [new file with mode: 0644]
lib/pgsql_shim/pgsqlinterface.hpp [new file with mode: 0644]

index 30116a6c8b1dac0a908e63845c2bfe984b76ee1a..9232ccb3a3f55035d6431e1c3bd8c46c11e8e0e3 100644 (file)
@@ -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
index f37bd7b1efa1379292196e227bc450a27721af68..4da7083670b15f205f66b2201bcf6ab1af1d302c 100644 (file)
@@ -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)
index 2b3d19e8441d679cef484fa16e555437503df992..ba9ee5d0eab8a946e46070ca228876ea26251c27 100644 (file)
@@ -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 (file)
index 0000000..c39ca40
--- /dev/null
@@ -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 (file)
index 0000000..9ef1fce
--- /dev/null
@@ -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 <memory>
+
+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<typename T>
+       T GetSymbolAddress(const String& name) const
+       {
+               static_assert(!std::is_same<T, void *>::value, "T must not be void *");
+
+               return reinterpret_cast<T>(GetSymbolAddress(name));
+       }
+
+private:
+       std::shared_ptr<LibraryHandle> m_Handle;
+};
+
+}
+
+#endif /* LIBRARY_H */
index 43709589ef4563b48e9a5cd5875b31bf960ae4da..83ebd879ba7b1afdfb4e28ef2cb01f75b9207af8 100644 (file)
@@ -18,6 +18,9 @@
  ******************************************************************************/
 
 #include "base/loader.hpp"
+#include "base/logger.hpp"
+#include "base/exception.hpp"
+#include "base/application.hpp"
 
 using namespace icinga;
 
index cc29c1dcdaeb4a5904d5bcb90d39ad6b5537906c..7b872895c3fb13d3191a5e3e12cff1518fc66560 100644 (file)
@@ -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;
 }
index d8db6fe2ca194a3c65248a66e0f47a082bf85c58..5892a9725d1829bda27f4645d3d9a540cefd8e85 100644 (file)
 # 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()
 
-  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})
 
-  add_library(db_ido_mysql STATIC ${db_ido_mysql_SOURCES})
+include_directories(${MYSQL_INCLUDE_DIR})
 
-  include_directories(${MYSQL_INCLUDE_DIR})
-  target_link_libraries(db_ido_mysql ${Boost_LIBRARIES} ${MYSQL_LIB} base config icinga db_ido)
+target_link_libraries(db_ido_mysql ${Boost_LIBRARIES} base config icinga db_ido)
 
-  set_target_properties (
-    db_ido_mysql PROPERTIES
-    FOLDER Components
-  )
+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_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
+  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"
-  )
+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.")
-endif()
+set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE)
index 972cae880635c647b6cf390187f340388323231c..69e3778b55916c326c49562947a3ea1376d83797 100644 (file)
@@ -46,6 +46,14 @@ void IdoMysqlConnection::OnConfigLoaded(void)
        ObjectImpl<IdoMysqlConnection>::OnConfigLoaded();
 
        m_QueryQueue.SetName("IdoMysqlConnection, " + GetName());
+
+       Library shimLibrary{"mysql_shim"};
+
+       auto create_mysql_shim = shimLibrary.GetSymbolAddress<create_mysql_shim_ptr>("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<IdoAsyncQuery>::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;
index 94a5120c892c9b0c1b2c223be954bf8863a302b0..2b0ce81bbbcf6db31737072a248eb1706637f8a3 100644 (file)
 #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 <mysql.h>
+#include "base/library.hpp"
 
 namespace icinga
 {
@@ -74,6 +75,9 @@ private:
 
        WorkQueue m_QueryQueue;
 
+       Library m_Library;
+       std::unique_ptr<MysqlInterface, MysqlInterfaceDeleter> m_Mysql;
+
        MYSQL m_Connection;
        int m_AffectedRows;
        unsigned int m_MaxPacketSize;
index d1eb092d616b0838412d29b1ed7ce6ade3a61bc9..a1b5e09132c57f2d8432658f99ee9b76b9638a6a 100644 (file)
 # 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)
-
-  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.")
+mkclass_target(idopgsqlconnection.ti idopgsqlconnection.tcpp 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})
+
+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)
index 3f44afa461d9dfcff985bab68f6acf8a551e1e3a..7ed0d717077d82fe00db8a3c0d237556fbcf1e47 100644 (file)
@@ -50,6 +50,14 @@ void IdoPgsqlConnection::OnConfigLoaded(void)
        ObjectImpl<IdoPgsqlConnection>::OnConfigLoaded();
 
        m_QueryQueue.SetName("IdoPgsqlConnection, " + GetName());
+
+       Library shimLibrary{"pgsql_shim"};
+
+       auto create_pgsql_shim = shimLibrary.GetSymbolAddress<create_pgsql_shim_ptr>("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;
index 9bad5af43a2407ffafe899049125717a6c7e8d43..577bdf3488205ad04e26732e0a8c01bf0a345f16 100644 (file)
 #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 <libpq-fe.h>
+#include "base/library.hpp"
 
 namespace icinga
 {
@@ -66,6 +67,9 @@ private:
 
        WorkQueue m_QueryQueue;
 
+       Library m_Library;
+       std::unique_ptr<PgsqlInterface, PgsqlInterfaceDeleter> 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 (file)
index 0000000..ceff898
--- /dev/null
@@ -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 (file)
index 0000000..ae36765
--- /dev/null
@@ -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 (file)
index 0000000..bbc9abe
--- /dev/null
@@ -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 (file)
index 0000000..77b4039
--- /dev/null
@@ -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 <memory>
+#include <mysql.h>
+
+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 (file)
index 0000000..20f09dc
--- /dev/null
@@ -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 (file)
index 0000000..7580d67
--- /dev/null
@@ -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 (file)
index 0000000..fef41f4
--- /dev/null
@@ -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 (file)
index 0000000..58a733a
--- /dev/null
@@ -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 <memory>
+#include <libpq-fe.h>
+
+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 */