Implement shim for libmysqlclient and libpq

This commit is contained in:
Gunnar Beutner 2018-01-01 15:21:14 +01:00
parent 90496b5456
commit 2f953d6204
21 changed files with 838 additions and 161 deletions

View File

@ -139,13 +139,6 @@ set(HAVE_EDITLINE "${EDITLINE_FOUND}")
find_package(Termcap) find_package(Termcap)
set(HAVE_TERMCAP "${TERMCAP_FOUND}") set(HAVE_TERMCAP "${TERMCAP_FOUND}")
find_package(PostgreSQL)
if(PostgreSQL_FOUND)
link_directories(${PostgreSQL_LIBRARY_DIRS})
include_directories(${PostgreSQL_INCLUDE_DIRS})
endif()
include_directories( include_directories(
${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib
${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/lib ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/lib

View File

@ -39,11 +39,25 @@ if(ICINGA2_WITH_MYSQL OR ICINGA2_WITH_PGSQL)
endif() endif()
if(ICINGA2_WITH_MYSQL) if(ICINGA2_WITH_MYSQL)
find_package(MySQL)
if(MYSQL_FOUND)
add_subdirectory(db_ido_mysql) 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() endif()
if(ICINGA2_WITH_PGSQL) if(ICINGA2_WITH_PGSQL)
find_package(PostgreSQL)
if(PostgreSQL_FOUND)
add_subdirectory(db_ido_pgsql) 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() endif()
if(ICINGA2_WITH_DEMO) if(ICINGA2_WITH_DEMO)

View File

@ -31,7 +31,7 @@ set(base_SOURCES
convert.cpp datetime.cpp datetime.thpp datetime-script.cpp debuginfo.cpp dictionary.cpp dictionary-script.cpp 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 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 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 netstring.cpp networkstream.cpp number.cpp number-script.cpp object.cpp
object-script.cpp objecttype.cpp primitivetype.cpp process.cpp ringbuffer.cpp scriptframe.cpp object-script.cpp objecttype.cpp primitivetype.cpp process.cpp ringbuffer.cpp scriptframe.cpp
function.cpp function.thpp function-script.cpp function.cpp function.thpp function-script.cpp

85
lib/base/library.cpp Normal file
View File

@ -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 */
}

58
lib/base/library.hpp Normal file
View File

@ -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 */

View File

@ -18,6 +18,9 @@
******************************************************************************/ ******************************************************************************/
#include "base/loader.hpp" #include "base/loader.hpp"
#include "base/logger.hpp"
#include "base/exception.hpp"
#include "base/application.hpp"
using namespace icinga; using namespace icinga;

View File

@ -842,7 +842,6 @@ ExpressionResult LibraryExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dh
Log(LogNotice, "config") Log(LogNotice, "config")
<< "Ignoring explicit load request for library \"" << libres << "\"."; << "Ignoring explicit load request for library \"" << libres << "\".";
//Loader::LoadExtensionLibrary(libres.GetValue());
return Empty; return Empty;
} }

View File

@ -15,9 +15,6 @@
# along with this program; if not, write to the Free Software Foundation # along with this program; if not, write to the Free Software Foundation
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
find_package(MySQL)
if(MYSQL_FOUND)
mkclass_target(idomysqlconnection.ti idomysqlconnection.tcpp idomysqlconnection.thpp) mkclass_target(idomysqlconnection.ti idomysqlconnection.tcpp idomysqlconnection.thpp)
set(db_ido_mysql_SOURCES set(db_ido_mysql_SOURCES
@ -31,7 +28,8 @@ if(MYSQL_FOUND)
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 ( set_target_properties (
db_ido_mysql PROPERTIES db_ido_mysql PROPERTIES
@ -56,6 +54,3 @@ if(MYSQL_FOUND)
) )
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE) 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()

View File

@ -46,6 +46,14 @@ void IdoMysqlConnection::OnConfigLoaded(void)
ObjectImpl<IdoMysqlConnection>::OnConfigLoaded(); ObjectImpl<IdoMysqlConnection>::OnConfigLoaded();
m_QueryQueue.SetName("IdoMysqlConnection, " + GetName()); 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) void IdoMysqlConnection::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata)
@ -98,7 +106,7 @@ void IdoMysqlConnection::Resume(void)
m_ReconnectTimer->Start(); m_ReconnectTimer->Start();
m_ReconnectTimer->Reschedule(0); m_ReconnectTimer->Reschedule(0);
ASSERT(mysql_thread_safe()); ASSERT(m_Mysql->thread_safe());
} }
void IdoMysqlConnection::Pause(void) void IdoMysqlConnection::Pause(void)
@ -127,7 +135,7 @@ void IdoMysqlConnection::ExceptionHandler(boost::exception_ptr exp)
<< "Exception during database operation: " << DiagnosticInformation(exp); << "Exception during database operation: " << DiagnosticInformation(exp);
if (GetConnected()) { if (GetConnected()) {
mysql_close(&m_Connection); m_Mysql->close(&m_Connection);
SetConnected(false); SetConnected(false);
} }
@ -146,7 +154,7 @@ void IdoMysqlConnection::Disconnect(void)
return; return;
Query("COMMIT"); Query("COMMIT");
mysql_close(&m_Connection); m_Mysql->close(&m_Connection);
SetConnected(false); SetConnected(false);
} }
@ -205,10 +213,10 @@ void IdoMysqlConnection::Reconnect(void)
if (GetConnected()) { if (GetConnected()) {
/* Check if we're really still connected */ /* Check if we're really still connected */
if (mysql_ping(&m_Connection) == 0) if (m_Mysql->ping(&m_Connection) == 0)
return; return;
mysql_close(&m_Connection); m_Mysql->close(&m_Connection);
SetConnected(false); SetConnected(false);
reconnect = true; reconnect = true;
} }
@ -249,7 +257,7 @@ void IdoMysqlConnection::Reconnect(void)
sslCipher = (!isslCipher.IsEmpty()) ? isslCipher.CStr() : nullptr; sslCipher = (!isslCipher.IsEmpty()) ? isslCipher.CStr() : nullptr;
/* connection */ /* connection */
if (!mysql_init(&m_Connection)) { if (!m_Mysql->init(&m_Connection)) {
Log(LogCritical, "IdoMysqlConnection") Log(LogCritical, "IdoMysqlConnection")
<< "mysql_init() failed: out of memory"; << "mysql_init() failed: out of memory";
@ -257,14 +265,14 @@ void IdoMysqlConnection::Reconnect(void)
} }
if (enableSsl) 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") Log(LogCritical, "IdoMysqlConnection")
<< "Connection to database '" << db << "' with user '" << user << "' on '" << host << ":" << port << "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); SetConnected(true);
@ -286,7 +294,7 @@ void IdoMysqlConnection::Reconnect(void)
row = FetchRow(result); row = FetchRow(result);
if (!row) { if (!row) {
mysql_close(&m_Connection); m_Mysql->close(&m_Connection);
SetConnected(false); SetConnected(false);
Log(LogCritical, "IdoMysqlConnection", "Schema does not provide any valid version! Verify your schema installation."); Log(LogCritical, "IdoMysqlConnection", "Schema does not provide any valid version! Verify your schema installation.");
@ -301,7 +309,7 @@ void IdoMysqlConnection::Reconnect(void)
SetSchemaVersion(version); SetSchemaVersion(version);
if (Utility::CompareVersion(IDO_COMPAT_SCHEMA_VERSION, version) < 0) { if (Utility::CompareVersion(IDO_COMPAT_SCHEMA_VERSION, version) < 0) {
mysql_close(&m_Connection); m_Mysql->close(&m_Connection);
SetConnected(false); SetConnected(false);
Log(LogCritical, "IdoMysqlConnection") Log(LogCritical, "IdoMysqlConnection")
@ -358,7 +366,7 @@ void IdoMysqlConnection::Reconnect(void)
<< "Last update by '" << endpoint_name << "' was " << status_update_age << "s ago."; << "Last update by '" << endpoint_name << "' was " << status_update_age << "s ago.";
if (status_update_age < GetFailoverTimeout()) { if (status_update_age < GetFailoverTimeout()) {
mysql_close(&m_Connection); m_Mysql->close(&m_Connection);
SetConnected(false); SetConnected(false);
SetShouldConnect(false); SetShouldConnect(false);
@ -370,7 +378,7 @@ void IdoMysqlConnection::Reconnect(void)
Log(LogNotice, "IdoMysqlConnection") Log(LogNotice, "IdoMysqlConnection")
<< "Local endpoint '" << my_endpoint->GetName() << "' is not authoritative, bailing out."; << "Local endpoint '" << my_endpoint->GetName() << "' is not authoritative, bailing out.";
mysql_close(&m_Connection); m_Mysql->close(&m_Connection);
SetConnected(false); SetConnected(false);
return; return;
@ -533,15 +541,15 @@ void IdoMysqlConnection::FinishAsyncQueries(void)
String query = querybuf.str(); String query = querybuf.str();
if (mysql_query(&m_Connection, query.CStr()) != 0) { if (m_Mysql->query(&m_Connection, query.CStr()) != 0) {
std::ostringstream msgbuf; std::ostringstream msgbuf;
String message = mysql_error(&m_Connection); String message = m_Mysql->error(&m_Connection);
msgbuf << "Error \"" << message << "\" when executing query \"" << query << "\""; msgbuf << "Error \"" << message << "\" when executing query \"" << query << "\"";
Log(LogCritical, "IdoMysqlConnection", msgbuf.str()); Log(LogCritical, "IdoMysqlConnection", msgbuf.str());
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
database_error() database_error()
<< errinfo_message(mysql_error(&m_Connection)) << errinfo_message(m_Mysql->error(&m_Connection))
<< errinfo_database_query(query) << errinfo_database_query(query)
); );
} }
@ -549,40 +557,40 @@ void IdoMysqlConnection::FinishAsyncQueries(void)
for (std::vector<IdoAsyncQuery>::size_type i = offset; i < offset + count; i++) { for (std::vector<IdoAsyncQuery>::size_type i = offset; i < offset + count; i++) {
const IdoAsyncQuery& aq = queries[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; IdoMysqlResult iresult;
if (!result) { if (!result) {
if (mysql_field_count(&m_Connection) > 0) { if (m_Mysql->field_count(&m_Connection) > 0) {
std::ostringstream msgbuf; std::ostringstream msgbuf;
String message = mysql_error(&m_Connection); String message = m_Mysql->error(&m_Connection);
msgbuf << "Error \"" << message << "\" when executing query \"" << aq.Query << "\""; msgbuf << "Error \"" << message << "\" when executing query \"" << aq.Query << "\"";
Log(LogCritical, "IdoMysqlConnection", msgbuf.str()); Log(LogCritical, "IdoMysqlConnection", msgbuf.str());
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
database_error() database_error()
<< errinfo_message(mysql_error(&m_Connection)) << errinfo_message(m_Mysql->error(&m_Connection))
<< errinfo_database_query(query) << errinfo_database_query(query)
); );
} }
} else } 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) if (aq.Callback)
aq.Callback(iresult); aq.Callback(iresult);
if (mysql_next_result(&m_Connection) > 0) { if (m_Mysql->next_result(&m_Connection) > 0) {
std::ostringstream msgbuf; std::ostringstream msgbuf;
String message = mysql_error(&m_Connection); String message = m_Mysql->error(&m_Connection);
msgbuf << "Error \"" << message << "\" when executing query \"" << query << "\""; msgbuf << "Error \"" << message << "\" when executing query \"" << query << "\"";
Log(LogCritical, "IdoMysqlConnection", msgbuf.str()); Log(LogCritical, "IdoMysqlConnection", msgbuf.str());
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
database_error() database_error()
<< errinfo_message(mysql_error(&m_Connection)) << errinfo_message(m_Mysql->error(&m_Connection))
<< errinfo_database_query(query) << errinfo_database_query(query)
); );
} }
@ -604,33 +612,33 @@ IdoMysqlResult IdoMysqlConnection::Query(const String& query)
IncreaseQueryCount(); IncreaseQueryCount();
if (mysql_query(&m_Connection, query.CStr()) != 0) { if (m_Mysql->query(&m_Connection, query.CStr()) != 0) {
std::ostringstream msgbuf; std::ostringstream msgbuf;
String message = mysql_error(&m_Connection); String message = m_Mysql->error(&m_Connection);
msgbuf << "Error \"" << message << "\" when executing query \"" << query << "\""; msgbuf << "Error \"" << message << "\" when executing query \"" << query << "\"";
Log(LogCritical, "IdoMysqlConnection", msgbuf.str()); Log(LogCritical, "IdoMysqlConnection", msgbuf.str());
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
database_error() database_error()
<< errinfo_message(mysql_error(&m_Connection)) << errinfo_message(m_Mysql->error(&m_Connection))
<< errinfo_database_query(query) << 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 (!result) {
if (mysql_field_count(&m_Connection) > 0) { if (m_Mysql->field_count(&m_Connection) > 0) {
std::ostringstream msgbuf; std::ostringstream msgbuf;
String message = mysql_error(&m_Connection); String message = m_Mysql->error(&m_Connection);
msgbuf << "Error \"" << message << "\" when executing query \"" << query << "\""; msgbuf << "Error \"" << message << "\" when executing query \"" << query << "\"";
Log(LogCritical, "IdoMysqlConnection", msgbuf.str()); Log(LogCritical, "IdoMysqlConnection", msgbuf.str());
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
database_error() database_error()
<< errinfo_message(mysql_error(&m_Connection)) << errinfo_message(m_Mysql->error(&m_Connection))
<< errinfo_database_query(query) << errinfo_database_query(query)
); );
} }
@ -638,14 +646,14 @@ IdoMysqlResult IdoMysqlConnection::Query(const String& query)
return IdoMysqlResult(); 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) DbReference IdoMysqlConnection::GetLastInsertID(void)
{ {
AssertOnWorkQueue(); AssertOnWorkQueue();
return DbReference(mysql_insert_id(&m_Connection)); return DbReference(m_Mysql->insert_id(&m_Connection));
} }
int IdoMysqlConnection::GetAffectedRows(void) int IdoMysqlConnection::GetAffectedRows(void)
@ -664,7 +672,7 @@ String IdoMysqlConnection::Escape(const String& s)
size_t length = utf8s.GetLength(); size_t length = utf8s.GetLength();
char *to = new char[utf8s.GetLength() * 2 + 1]; 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); String result = String(to);
@ -681,20 +689,20 @@ Dictionary::Ptr IdoMysqlConnection::FetchRow(const IdoMysqlResult& result)
MYSQL_FIELD *field; MYSQL_FIELD *field;
unsigned long *lengths, i; unsigned long *lengths, i;
row = mysql_fetch_row(result.get()); row = m_Mysql->fetch_row(result.get());
if (!row) if (!row)
return nullptr; return nullptr;
lengths = mysql_fetch_lengths(result.get()); lengths = m_Mysql->fetch_lengths(result.get());
if (!lengths) if (!lengths)
return nullptr; return nullptr;
Dictionary::Ptr dict = new Dictionary(); Dictionary::Ptr dict = new Dictionary();
mysql_field_seek(result.get(), 0); m_Mysql->field_seek(result.get(), 0);
for (field = mysql_fetch_field(result.get()), i = 0; field; field = mysql_fetch_field(result.get()), i++) 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])); dict->Set(field->name, String(row[i], row[i] + lengths[i]));
return dict; return dict;

View File

@ -21,10 +21,11 @@
#define IDOMYSQLCONNECTION_H #define IDOMYSQLCONNECTION_H
#include "db_ido_mysql/idomysqlconnection.thpp" #include "db_ido_mysql/idomysqlconnection.thpp"
#include "mysql_shim/mysqlinterface.hpp"
#include "base/array.hpp" #include "base/array.hpp"
#include "base/timer.hpp" #include "base/timer.hpp"
#include "base/workqueue.hpp" #include "base/workqueue.hpp"
#include <mysql.h> #include "base/library.hpp"
namespace icinga namespace icinga
{ {
@ -74,6 +75,9 @@ private:
WorkQueue m_QueryQueue; WorkQueue m_QueryQueue;
Library m_Library;
std::unique_ptr<MysqlInterface, MysqlInterfaceDeleter> m_Mysql;
MYSQL m_Connection; MYSQL m_Connection;
int m_AffectedRows; int m_AffectedRows;
unsigned int m_MaxPacketSize; unsigned int m_MaxPacketSize;

View File

@ -15,7 +15,6 @@
# along with this program; if not, write to the Free Software Foundation # along with this program; if not, write to the Free Software Foundation
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. # 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 set(db_ido_pgsql_SOURCES
@ -28,7 +27,9 @@ if(PostgreSQL_FOUND)
add_library(db_ido_pgsql STATIC ${db_ido_pgsql_SOURCES}) 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) include_directories(${PostgreSQL_INCLUDE_DIRS})
target_link_libraries(db_ido_pgsql ${Boost_LIBRARIES} base config icinga db_ido)
set_target_properties ( set_target_properties (
db_ido_pgsql PROPERTIES db_ido_pgsql PROPERTIES
@ -53,6 +54,3 @@ if(PostgreSQL_FOUND)
) )
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE) 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.")
endif()

View File

@ -50,6 +50,14 @@ void IdoPgsqlConnection::OnConfigLoaded(void)
ObjectImpl<IdoPgsqlConnection>::OnConfigLoaded(); ObjectImpl<IdoPgsqlConnection>::OnConfigLoaded();
m_QueryQueue.SetName("IdoPgsqlConnection, " + GetName()); 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) void IdoPgsqlConnection::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata)
@ -102,7 +110,7 @@ void IdoPgsqlConnection::Resume(void)
m_ReconnectTimer->Start(); m_ReconnectTimer->Start();
m_ReconnectTimer->Reschedule(0); m_ReconnectTimer->Reschedule(0);
ASSERT(PQisthreadsafe()); ASSERT(m_Pgsql->isthreadsafe());
} }
void IdoPgsqlConnection::Pause(void) void IdoPgsqlConnection::Pause(void)
@ -126,7 +134,7 @@ void IdoPgsqlConnection::ExceptionHandler(boost::exception_ptr exp)
<< "Exception during database operation: " << DiagnosticInformation(exp); << "Exception during database operation: " << DiagnosticInformation(exp);
if (GetConnected()) { if (GetConnected()) {
PQfinish(m_Connection); m_Pgsql->finish(m_Connection);
SetConnected(false); SetConnected(false);
} }
} }
@ -145,7 +153,7 @@ void IdoPgsqlConnection::Disconnect(void)
Query("COMMIT"); Query("COMMIT");
PQfinish(m_Connection); m_Pgsql->finish(m_Connection);
SetConnected(false); SetConnected(false);
} }
@ -193,7 +201,7 @@ void IdoPgsqlConnection::Reconnect(void)
Query("SELECT 1"); Query("SELECT 1");
return; return;
} catch (const std::exception&) { } catch (const std::exception&) {
PQfinish(m_Connection); m_Pgsql->finish(m_Connection);
SetConnected(false); SetConnected(false);
reconnect = true; reconnect = true;
} }
@ -216,14 +224,14 @@ void IdoPgsqlConnection::Reconnect(void)
passwd = (!ipasswd.IsEmpty()) ? ipasswd.CStr() : nullptr; passwd = (!ipasswd.IsEmpty()) ? ipasswd.CStr() : nullptr;
db = (!idb.IsEmpty()) ? idb.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) if (!m_Connection)
return; return;
if (PQstatus(m_Connection) != CONNECTION_OK) { if (m_Pgsql->status(m_Connection) != CONNECTION_OK) {
String message = PQerrorMessage(m_Connection); String message = m_Pgsql->errorMessage(m_Connection);
PQfinish(m_Connection); m_Pgsql->finish(m_Connection);
SetConnected(false); SetConnected(false);
Log(LogCritical, "IdoPgsqlConnection") Log(LogCritical, "IdoPgsqlConnection")
@ -240,7 +248,7 @@ void IdoPgsqlConnection::Reconnect(void)
/* explicitely require legacy mode for string escaping in PostgreSQL >= 9.1 /* explicitely require legacy mode for string escaping in PostgreSQL >= 9.1
* changing standard_conforming_strings to on by default * 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"); result = Query("SET standard_conforming_strings TO off");
String dbVersionName = "idoutils"; String dbVersionName = "idoutils";
@ -249,7 +257,7 @@ void IdoPgsqlConnection::Reconnect(void)
Dictionary::Ptr row = FetchRow(result, 0); Dictionary::Ptr row = FetchRow(result, 0);
if (!row) { if (!row) {
PQfinish(m_Connection); m_Pgsql->finish(m_Connection);
SetConnected(false); SetConnected(false);
Log(LogCritical, "IdoPgsqlConnection", "Schema does not provide any valid version! Verify your schema installation."); Log(LogCritical, "IdoPgsqlConnection", "Schema does not provide any valid version! Verify your schema installation.");
@ -262,7 +270,7 @@ void IdoPgsqlConnection::Reconnect(void)
SetSchemaVersion(version); SetSchemaVersion(version);
if (Utility::CompareVersion(IDO_COMPAT_SCHEMA_VERSION, version) < 0) { if (Utility::CompareVersion(IDO_COMPAT_SCHEMA_VERSION, version) < 0) {
PQfinish(m_Connection); m_Pgsql->finish(m_Connection);
SetConnected(false); SetConnected(false);
Log(LogCritical, "IdoPgsqlConnection") Log(LogCritical, "IdoPgsqlConnection")
@ -316,7 +324,7 @@ void IdoPgsqlConnection::Reconnect(void)
<< "Last update by '" << endpoint_name << "' was " << status_update_age << "s ago."; << "Last update by '" << endpoint_name << "' was " << status_update_age << "s ago.";
if (status_update_age < GetFailoverTimeout()) { if (status_update_age < GetFailoverTimeout()) {
PQfinish(m_Connection); m_Pgsql->finish(m_Connection);
SetConnected(false); SetConnected(false);
SetShouldConnect(false); SetShouldConnect(false);
@ -328,7 +336,7 @@ void IdoPgsqlConnection::Reconnect(void)
Log(LogNotice, "IdoPgsqlConnection") Log(LogNotice, "IdoPgsqlConnection")
<< "Local endpoint '" << my_endpoint->GetName() << "' is not authoritative, bailing out."; << "Local endpoint '" << my_endpoint->GetName() << "' is not authoritative, bailing out.";
PQfinish(m_Connection); m_Pgsql->finish(m_Connection);
SetConnected(false); SetConnected(false);
return; return;
@ -437,10 +445,10 @@ IdoPgsqlResult IdoPgsqlConnection::Query(const String& query)
IncreaseQueryCount(); IncreaseQueryCount();
PGresult *result = PQexec(m_Connection, query.CStr()); PGresult *result = m_Pgsql->exec(m_Connection, query.CStr());
if (!result) { if (!result) {
String message = PQerrorMessage(m_Connection); String message = m_Pgsql->errorMessage(m_Connection);
Log(LogCritical, "IdoPgsqlConnection") Log(LogCritical, "IdoPgsqlConnection")
<< "Error \"" << message << "\" when executing query \"" << query << "\""; << "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); m_AffectedRows = atoi(rowCount);
if (PQresultStatus(result) == PGRES_COMMAND_OK) { if (m_Pgsql->resultStatus(result) == PGRES_COMMAND_OK) {
PQclear(result); m_Pgsql->clear(result);
return IdoPgsqlResult(); return IdoPgsqlResult();
} }
if (PQresultStatus(result) != PGRES_TUPLES_OK) { if (m_Pgsql->resultStatus(result) != PGRES_TUPLES_OK) {
String message = PQresultErrorMessage(result); String message = m_Pgsql->resultErrorMessage(result);
PQclear(result); m_Pgsql->clear(result);
Log(LogCritical, "IdoPgsqlConnection") Log(LogCritical, "IdoPgsqlConnection")
<< "Error \"" << message << "\" when executing query \"" << query << "\""; << "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) DbReference IdoPgsqlConnection::GetSequenceValue(const String& table, const String& column)
@ -508,7 +516,7 @@ String IdoPgsqlConnection::Escape(const String& s)
size_t length = utf8s.GetLength(); size_t length = utf8s.GetLength();
char *to = new char[utf8s.GetLength() * 2 + 1]; 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); String result = String(to);
@ -521,20 +529,20 @@ Dictionary::Ptr IdoPgsqlConnection::FetchRow(const IdoPgsqlResult& result, int r
{ {
AssertOnWorkQueue(); AssertOnWorkQueue();
if (row >= PQntuples(result.get())) if (row >= m_Pgsql->ntuples(result.get()))
return nullptr; return nullptr;
int columns = PQnfields(result.get()); int columns = m_Pgsql->nfields(result.get());
Dictionary::Ptr dict = new Dictionary(); Dictionary::Ptr dict = new Dictionary();
for (int column = 0; column < columns; column++) { for (int column = 0; column < columns; column++) {
Value value; Value value;
if (!PQgetisnull(result.get(), row, column)) if (!m_Pgsql->getisnull(result.get(), row, column))
value = PQgetvalue(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; return dict;

View File

@ -21,10 +21,11 @@
#define IDOPGSQLCONNECTION_H #define IDOPGSQLCONNECTION_H
#include "db_ido_pgsql/idopgsqlconnection.thpp" #include "db_ido_pgsql/idopgsqlconnection.thpp"
#include "pgsql_shim/pgsqlinterface.hpp"
#include "base/array.hpp" #include "base/array.hpp"
#include "base/timer.hpp" #include "base/timer.hpp"
#include "base/workqueue.hpp" #include "base/workqueue.hpp"
#include <libpq-fe.h> #include "base/library.hpp"
namespace icinga namespace icinga
{ {
@ -66,6 +67,9 @@ private:
WorkQueue m_QueryQueue; WorkQueue m_QueryQueue;
Library m_Library;
std::unique_ptr<PgsqlInterface, PgsqlInterfaceDeleter> m_Pgsql;
PGconn *m_Connection; PGconn *m_Connection;
int m_AffectedRows; int m_AffectedRows;

View File

@ -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
)

View File

@ -0,0 +1,3 @@
LIBRARY mysql_shim
EXPORTS
create_mysql_shim

View File

@ -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();
}

View File

@ -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 */

View File

@ -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
)

View File

@ -0,0 +1,3 @@
LIBRARY pgsql_shim
EXPORTS
create_pgsql_shim

View File

@ -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();
}

View File

@ -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 */