/* * sqlacct.cxx * * SQL accounting module for GNU Gatekeeper * * Copyright (c) 2004, Michal Zygmuntowicz * * This work is published under the GNU Public License (GPL) * see file COPYING for details. * We also explicitely grant the right to link this code * with the OpenH323 library. * * $Log: sqlacct.cxx,v $ * Revision 1.13 2006/04/14 13:56:19 willamowius * call failover code merged * * Revision 1.1.1.1 2005/11/21 20:20:00 willamowius * * * Revision 1.4 2005/11/15 19:52:56 jan * Michal v1 (works, but on in routed, not proxy mode) * * Revision 1.12 2005/05/19 16:41:17 zvision * Solaris need explicit std::map * * Revision 1.11 2005/04/24 16:39:45 zvision * MSVC6.0 compatibility fixed * * Revision 1.10 2005/03/15 11:49:38 zvision * Make reconnect working correctly when a database server is down * * Revision 1.9 2005/03/08 00:13:47 zvision * Support for connect event in SqlAcct module, thanks to Boian Bonev * * Revision 1.8 2005/01/12 17:55:07 willamowius * fix gkip accounting parameter * * Revision 1.7 2005/01/05 15:42:41 willamowius * new accounting event 'connect', parameter substitution unified in parent class * * Revision 1.6 2005/01/04 18:13:42 willamowius * space in trace msg * * Revision 1.5 2004/12/15 14:43:25 zvision * Shutdown the gatekeeper on SQL auth/acct module config errors. * Thanks to Mikko Oilinki. * * Revision 1.4 2004/11/15 23:57:43 zvision * Ability to choose between the original and the rewritten dialed number * * Revision 1.3 2004/11/10 18:30:41 zvision * Ability to customize timestamp strings * * Revision 1.2 2004/07/09 22:11:36 zvision * SQLAcct module ported from 2.0 branch * * Revision 1.1.2.6 2004/06/22 18:41:17 zvision * Username, Calling-Station-Id and Called-Station-Id handling rewritten. * Radius modules optimized. * * Revision 1.1.2.5 2004/06/18 15:42:51 zvision * Better User-Name and Calling-Station-Id handling for unregistered endpoints * * Revision 1.1.2.4 2004/06/06 12:31:04 zvision * New SQLAcct/FileAcct parameters. Thanks to Patrick! * * Revision 1.1.2.3 2004/05/12 14:00:48 zvision * Header file usage more consistent. Solaris std::map problems fixed. * Compilation warnings removed. VSNET2003 project files added. ANSI.h removed. * * Revision 1.1.2.2 2004/04/24 10:31:57 zvision * Use baseclass GetConfigSectionName * * Revision 1.1.2.1 2004/04/23 16:01:16 zvision * New direct SQL accounting module (SQLAcct) * */ #if defined(_WIN32) && (_MSC_VER <= 1200) #pragma warning(disable:4786) // warning about too long debug symbol off #pragma warning(disable:4284) #endif #include #include #include #include "gk_const.h" #include "h323util.h" #include "Toolkit.h" #include "RasTbl.h" #include "RasSrv.h" #include "gksql.h" #include "gkacct.h" #include "sqlacct.h" using std::vector; SQLAcct::SQLAcct( const char* moduleName, const char* cfgSecName ) : GkAcctLogger(moduleName, cfgSecName), m_sqlConn(NULL) { SetSupportedEvents(SQLAcctEvents); PConfig* const cfg = GkConfig(); const PString& cfgSec = GetConfigSectionName(); const PString driverName = cfg->GetString(cfgSec, "Driver", ""); if (driverName.IsEmpty()) { PTRACE(0, "GKACCT\t" << GetName() << " module creation failed: " "no SQL driver selected" ); PTRACE(0, "GKACCT\tFATAL: Shutting down"); RasServer::Instance()->Stop(); return; } m_sqlConn = GkSQLConnection::Create(driverName, cfgSec); if (m_sqlConn == NULL) { PTRACE(0, "GKACCT\t" << GetName() << " module creation failed: " "could not find " << driverName << " database driver" ); PTRACE(0, "GKACCT\tFATAL: Shutting down"); RasServer::Instance()->Stop(); return; } m_startQuery = cfg->GetString(cfgSec, "StartQuery", ""); if (m_startQuery.IsEmpty() && (GetEnabledEvents() & GetSupportedEvents() & AcctStart) == AcctStart) { PTRACE(0, "GKACCT\t" << GetName() << " module creation failed: " "no start query configured" ); PTRACE(0, "GKACCT\tFATAL: Shutting down"); RasServer::Instance()->Stop(); return; } else PTRACE(4, "GKACCT\t" << GetName() << " start query: " << m_startQuery); m_startQueryAlt = cfg->GetString(cfgSec, "StartQueryAlt", ""); if (!m_startQueryAlt) PTRACE(4, "GKACCT\t" << GetName() << " alternative start query: " << m_startQueryAlt ); m_updateQuery = cfg->GetString(cfgSec, "UpdateQuery", ""); if (m_updateQuery.IsEmpty() && (GetEnabledEvents() & GetSupportedEvents() & (AcctUpdate | AcctConnect)) != 0) { PTRACE(0, "GKACCT\t" << GetName() << " module creation failed: " "no update query configured" ); PTRACE(0, "GKACCT\tFATAL: Shutting down"); RasServer::Instance()->Stop(); return; } else PTRACE(4, "GKACCT\t" << GetName() << " update query: " << m_updateQuery); m_stopQuery = cfg->GetString(cfgSec, "StopQuery", ""); if (m_stopQuery.IsEmpty() && (GetEnabledEvents() & GetSupportedEvents() & AcctStop) == AcctStop) { PTRACE(0, "GKACCT\t" << GetName() << " module creation failed: " "no stop query configured" ); PTRACE(0, "GKACCT\tFATAL: Shutting down"); RasServer::Instance()->Stop(); return; } else PTRACE(4, "GKACCT\t" << GetName() << " stop query: " << m_stopQuery); m_stopQueryAlt = cfg->GetString(cfgSec, "StopQueryAlt", ""); if (!m_stopQueryAlt) PTRACE(4, "GKACCT\t" << GetName() << " alternative stop query: " << m_stopQueryAlt ); vector interfaces; Toolkit::Instance()->GetGKHome(interfaces); if (interfaces.empty()) { PTRACE(0, "GKACCT\t" << GetName() << " cannot determine gatekeeper IP address"); PTRACE(0, "GKACCT\tFATAL: Shutting down"); RasServer::Instance()->Stop(); return; } if (!m_sqlConn->Initialize(cfg, cfgSec)) { PTRACE(0, "GKACCT\t" << GetName() << " module creation failed: " "could not connect to the database" ); PTRACE(0, "GKACCT\tFATAL: Shutting down"); RasServer::Instance()->Stop(); return; } m_timestampFormat = cfg->GetString(cfgSec, "TimestampFormat", ""); } SQLAcct::~SQLAcct() { delete m_sqlConn; } GkAcctLogger::Status SQLAcct::Log( GkAcctLogger::AcctEvent evt, const callptr& call ) { if ((evt & GetEnabledEvents() & GetSupportedEvents()) == 0) return Next; if (!call) { PTRACE(1, "GKACCT\t" << GetName() << " - missing call info for event " << evt); return Fail; } const long callNumber = call->GetCallNumber(); if (m_sqlConn == NULL) { PTRACE(2, "GKACCT\t" << GetName() << " failed to store accounting " "data (event: " << evt << ", call: " << callNumber << "): SQL connection not active" ); return Fail; } PString query, queryAlt; if (evt == AcctStart) { query = m_startQuery; queryAlt = m_startQueryAlt; } else if (evt == AcctUpdate || evt == AcctConnect) query = m_updateQuery; else if (evt == AcctStop) { query = m_stopQuery; queryAlt = m_stopQueryAlt; } if (query.IsEmpty()) { PTRACE(2, "GKACCT\t" << GetName() << " failed to store accounting " "data (event: " << evt << ", call: " << callNumber << "): SQL query is empty" ); return Fail; } std::map params; SetupAcctParams(params, call, m_timestampFormat); GkSQLResult* result = m_sqlConn->ExecuteQuery(query, params); if (result == NULL) PTRACE(2, "GKACCT\t" << GetName() << " failed to store accounting " "data (event: " << evt << ", call: " << callNumber << "): timeout or fatal error" ); if (result) { if (result->IsValid()) { if (result->GetNumRows() < 1) { PTRACE(4, "GKACCT\t" << GetName() << " failed to store accounting " "data (event: " << evt << ", call: " << callNumber << "): no rows have been updated" ); delete result; result = NULL; } } else { PTRACE(2, "GKACCT\t" << GetName() << " failed to store accounting " "data (event: " << evt << ", call: " << callNumber << "): (" << result->GetErrorCode() << ") " << result->GetErrorMessage() ); delete result; result = NULL; } } if (result == NULL && !queryAlt) { result = m_sqlConn->ExecuteQuery(queryAlt, params); if (result == NULL) PTRACE(2, "GKACCT\t" << GetName() << " failed to store accounting " "data (event: " << evt << ", call: " << callNumber << "): timeout or fatal error" ); else { if (result->IsValid()) { if (result->GetNumRows() < 1) PTRACE(4, "GKACCT\t" << GetName() << " failed to store accounting " "data (event: " << evt << ", call: " << callNumber << "): no rows have been updated" ); } else PTRACE(2, "GKACCT\t" << GetName() << " failed to store accounting " "data (event: " << evt << ", call: " << callNumber << "): (" << result->GetErrorCode() << ") " << result->GetErrorMessage() ); } } const bool succeeded = result != NULL && result->IsValid(); delete result; return succeeded ? Ok : Fail; } namespace { GkAcctLoggerCreator SQLAcctLoggerCreator("SQLAcct"); }