8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 22:03:03 +01:00

Added timer-related interfaces. Added helper method to wait for plugin to be released on shutdown. Misc stability changes in PluginManager.

This commit is contained in:
alexpeshkoff 2011-03-16 11:04:04 +00:00
parent 7998963202
commit f6914b8db0
23 changed files with 1071 additions and 363 deletions

View File

@ -0,0 +1,66 @@
/*
* PROGRAM: JRD Access Method
* MODULE: LegacyHash.h
* DESCRIPTION: Firebird 2.X style hash
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
* 2003.02.02 Dmitry Yemanov: Implemented cached security database connection
*/
#ifndef AUTH_LEGACY_HASH_H
#define AUTH_LEGACY_HASH_H
#include "../common/utils_proto.h"
#include "../common/sha.h"
namespace Auth {
const size_t MAX_PASSWORD_LENGTH = 64; // used to store passwords internally
static const char* const PASSWORD_SALT = "9z"; // for old ENC_crypt()
const size_t SALT_LENGTH = 12; // measured after base64 coding
class LegacyHash
{
public:
static void hash(Firebird::string& h, const Firebird::string& userName, const TEXT* passwd)
{
Firebird::string salt;
Jrd::CryptSupport::random(salt, SALT_LENGTH);
hash(h, userName, passwd, salt);
}
static void hash(Firebird::string& h,
const Firebird::string& userName,
const Firebird::string& passwd,
const Firebird::string& oldHash)
{
Firebird::string salt(oldHash);
salt.resize(SALT_LENGTH, '=');
Firebird::string allData(salt);
allData += userName;
allData += passwd;
Jrd::CryptSupport::hash(h, allData);
h = salt + h;
}
};
} // namespace Auth
#endif // AUTH_LEGACY_SERVER_H

View File

@ -29,7 +29,7 @@
#include <ctype.h>
#include "../common/common.h"
#include "../jrd/ibase.h"
#include "../auth/SecurityDatabase/LegacyServer.h"
#include "../auth/SecurityDatabase/LegacyHash.h"
#include "../common/enc_proto.h"
#include "../yvalve/gds_proto.h"
#include "../common/isc_proto.h"
@ -39,6 +39,7 @@
#include "../common/classes/UserBlob.h"
#include "../auth/SecurityDatabase/LegacyManagement.h"
#include "../common/classes/ImplementHelper.h"
#include "../common/classes/ClumpletWriter.h"
#include "FirebirdPluginApi.h"
// Here we use version-independent symbolic link (or copy) of actual database
@ -377,7 +378,7 @@ int FB_CARG SecurityDatabaseManagement::execute(Firebird::Status* st, User* user
if (user->password()->entered())
{
ENC_crypt(encrypted1, sizeof encrypted1, user->password()->get(), PASSWORD_SALT);
SecurityDatabase::hash(encrypted2, user->userName()->get(), &encrypted1[2]);
LegacyHash::hash(encrypted2, user->userName()->get(), &encrypted1[2]);
STR_STORE(U.PLG$PASSWD, encrypted2.c_str());
U.PLG$PASSWD.NULL = ISC_FALSE;
}
@ -452,7 +453,7 @@ int FB_CARG SecurityDatabaseManagement::execute(Firebird::Status* st, User* user
if (user->password()->entered())
{
ENC_crypt(encrypted1, sizeof encrypted1, user->password()->get(), PASSWORD_SALT);
SecurityDatabase::hash(encrypted2, user->userName()->get(), &encrypted1[2]);
LegacyHash::hash(encrypted2, user->userName()->get(), &encrypted1[2]);
STR_STORE(U.PLG$PASSWD, encrypted2.c_str());
U.PLG$PASSWD.NULL = ISC_FALSE;
}
@ -667,6 +668,7 @@ int FB_CARG SecurityDatabaseManagement::execute(Firebird::Status* st, User* user
// register plugin
static Firebird::SimpleFactory<Auth::SecurityDatabaseManagement> factory;
static Firebird::UnloadDetector unloadDetector;
extern "C" void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master)
{
@ -674,6 +676,7 @@ extern "C" void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master)
factory->addRef();
iPlugin->registerPlugin(Firebird::PluginType::AuthUserManagement, "Legacy_Auth", &factory);
iPlugin->setModuleCleanup(&unloadDetector);
iPlugin->release();
}

View File

@ -1,6 +1,6 @@
/*
* PROGRAM: JRD Access Method
* MODULE: pwd.cpp
* MODULE: LegacyServer.cpp
* DESCRIPTION: User information database access
*
* The contents of this file are subject to the Interbase Public
@ -31,6 +31,7 @@
#include "../jrd/ibase.h"
#include "../jrd/jrd.h"
#include "../auth/SecurityDatabase/LegacyServer.h"
#include "../auth/SecurityDatabase/LegacyHash.h"
#include "../common/enc_proto.h"
#include "../jrd/err_proto.h"
#include "../yvalve/gds_proto.h"
@ -42,89 +43,12 @@
#include "../common/classes/objects_array.h"
#include "../common/classes/init.h"
#include "../common/classes/ImplementHelper.h"
#include "Timer.h"
using namespace Firebird;
namespace {
// temporal implementation of timer
GlobalPtr<Mutex> timerMutex;
FPTR_VOID_PTR toRun = 0;
unsigned int cnt = 0;
int active = 0;
int stopTimer(const int, const int mask, void*)
{
switch(mask)
{
case fb_shut_preproviders:
active = 2;
break;
case fb_shut_finish:
while (active == 2)
{
THREAD_SLEEP(10);
}
break;
}
return 0;
}
THREAD_ENTRY_DECLARE threadTimer(THREAD_ENTRY_PARAM)
{
while (active == 1)
{
{ // scope
MutexLockGuard g(timerMutex);
if (cnt == 0)
{
if (toRun)
{
toRun(0);
toRun = 0;
}
}
else
{
--cnt;
}
}
THREAD_SLEEP(100);
}
active = 3;
return 0;
}
int fb_alloc_timer()
{
if (! active)
{
active = 1;
Thread::start(threadTimer, 0, 0);
fb_shutdown_callback(0, stopTimer, fb_shut_preproviders | fb_shut_finish, 0);
}
return 1;
}
void fb_thread_timer(int, int delay, FPTR_VOID_PTR function, void*)
{
MutexLockGuard g(timerMutex);
cnt = delay / 100;
if (! cnt)
{
cnt = 1;
}
toRun = function;
}
// BLR to search database for user name record
const UCHAR PWD_REQUEST[] =
@ -194,12 +118,52 @@ const UCHAR TPB[4] =
isc_tpb_wait
};
int timer = 0;
} // anonymous
} // anonymous namespace
namespace Auth {
class SecurityDatabase : public Firebird::StdIface<Firebird::ITimer, FB_I_TIMER_VERSION>
{
public:
Result verify(WriterInterface* authBlock,
Firebird::ClumpletReader& originalDpb);
static int shutdown(const int, const int, void*);
char secureDbName[MAXPATHLEN];
SecurityDatabase()
: lookup_db(0), lookup_req(0)
{
}
private:
void FB_CARG handler();
int FB_CARG release()
{
if (--refCounter == 0)
{
delete this;
return 0;
}
return 1;
}
Firebird::Mutex mutex;
ISC_STATUS_ARRAY status;
isc_db_handle lookup_db;
isc_req_handle lookup_req;
void fini();
bool lookup_user(const char*, char*);
void prepare();
void checkStatus(const char* callName, ISC_STATUS userError = isc_psw_db_error);
};
/******************************************************************************
*
* Private interface
@ -229,14 +193,6 @@ void SecurityDatabase::fini()
}
}
void SecurityDatabase::init()
{
if (! timer)
{
timer = fb_alloc_timer();
}
}
bool SecurityDatabase::lookup_user(const char* user_name, char* pwd)
{
bool found = false; // user found flag
@ -297,7 +253,7 @@ void SecurityDatabase::prepare()
return;
}
init();
fb_shutdown_callback(status, shutdown, fb_shut_preproviders, 0);
lookup_db = lookup_req = 0;
@ -394,7 +350,7 @@ Result SecurityDatabase::verify(WriterInterface* authBlock,
}
string newHash;
hash(newHash, login, passwordEnc, storedHash);
LegacyHash::hash(newHash, login, passwordEnc, storedHash);
if (newHash != storedHash)
{
bool legacyHash = Config::getLegacyHash();
@ -440,14 +396,40 @@ void SecurityDatabase::checkStatus(const char* callName, ISC_STATUS userError)
#endif
}
// TODO - avoid races between timer thread and auth thread
// TODO - account for too old instances and cleanup them
// WHEN - when timer interface is ready
typedef HalfStaticArray<SecurityDatabase*, 4> InstancesArray;
GlobalPtr<InstancesArray> instances;
GlobalPtr<Mutex> instancesMutex;
void SecurityDatabase::shutdown(void*)
void FB_CARG SecurityDatabase::handler()
{
try
{
MutexLockGuard g(instancesMutex);
fini();
InstancesArray& curInstances(instances);
for (unsigned int i = 0; i < curInstances.getCount(); ++i)
{
if (curInstances[i] == this)
{
curInstances.remove(i);
break;
}
}
release();
}
catch (Exception &ex)
{
ISC_STATUS_ARRAY status;
ex.stuff_exception(status);
if (status[0] == 1 && status[1] != isc_att_shutdown)
{
iscLogStatus("Legacy security database shutdown", status);
}
}
}
int SecurityDatabase::shutdown(const int, const int, void*)
{
try
{
@ -458,7 +440,7 @@ void SecurityDatabase::shutdown(void*)
if (curInstances[i])
{
curInstances[i]->fini();
delete curInstances[i];
curInstances[i]->release();
curInstances[i] = NULL;
}
}
@ -472,7 +454,11 @@ void SecurityDatabase::shutdown(void*)
{
iscLogStatus("Legacy security database shutdown", status);
}
return FB_FAILURE;
}
return FB_SUCCESS;
}
const static unsigned int INIT_KEY = ((~0) - 1);
@ -507,7 +493,6 @@ Result SecurityDatabaseServer::startAuthentication(Firebird::Status* status,
SecurityDatabase* instance = 0;
fb_thread_timer(timer, 10000, SecurityDatabase::shutdown, 0);
{ // guard scope
MutexLockGuard g(instancesMutex);
InstancesArray& curInstances(instances);
@ -531,7 +516,8 @@ Result SecurityDatabaseServer::startAuthentication(Firebird::Status* status,
fb_assert(instance);
ClumpletReader rdr(isService ? ClumpletReader::spbList : ClumpletReader::dpbList, dpb, dpbSize);
return instance->verify(writerInterface, rdr);
Result rc = instance->verify(writerInterface, rdr);
Firebird::TimerInterface()->start(instance, 10 * 1000 * 1000);
}
catch (const Firebird::Exception& ex)
{

View File

@ -1,6 +1,6 @@
/*
* PROGRAM: JRD Access Method
* MODULE: jrd_pwd.h
* MODULE: LegacyServer.h
* DESCRIPTION: User information database name
*
* The contents of this file are subject to the Interbase Public
@ -43,61 +43,6 @@
namespace Auth {
const size_t MAX_PASSWORD_LENGTH = 64; // used to store passwords internally
static const char* const PASSWORD_SALT = "9z"; // for old ENC_crypt()
const size_t SALT_LENGTH = 12; // measured after base64 coding
class SecurityDatabase : public Firebird::GlobalStorage
{
public:
Result verify(WriterInterface* authBlock,
Firebird::ClumpletReader& originalDpb);
static void shutdown(void*);
static void hash(Firebird::string& h, const Firebird::string& userName, const TEXT* passwd)
{
Firebird::string salt;
Jrd::CryptSupport::random(salt, SALT_LENGTH);
hash(h, userName, passwd, salt);
}
static void hash(Firebird::string& h,
const Firebird::string& userName,
const Firebird::string& passwd,
const Firebird::string& oldHash)
{
Firebird::string salt(oldHash);
salt.resize(SALT_LENGTH, '=');
Firebird::string allData(salt);
allData += userName;
allData += passwd;
Jrd::CryptSupport::hash(h, allData);
h = salt + h;
}
char secureDbName[MAXPATHLEN];
SecurityDatabase()
: lookup_db(0), lookup_req(0)
{
}
private:
Firebird::Mutex mutex;
ISC_STATUS_ARRAY status;
isc_db_handle lookup_db;
isc_req_handle lookup_req;
void init();
void fini();
bool lookup_user(const char*, char*);
void prepare();
void checkStatus(const char* callName, ISC_STATUS userError = isc_psw_db_error);
};
class SecurityDatabaseServerFactory : public Firebird::StdIface<Firebird::PluginsFactory, FB_PLUGINS_FACTORY_VERSION>
{
public:

View File

@ -30,6 +30,7 @@
#define FB_COMMON_CLASSES_IMPLEMENT_HELPER
#include "FirebirdPluginApi.h"
#include "Timer.h"
#include "../common/classes/alloc.h"
#include "gen/iberror.h"
#include "../yvalve/gds_proto.h"
@ -170,6 +171,15 @@ class SimpleFactory : public Static<SimpleFactoryBase<P> >
};
// Master interface
class MasterInterface : public AutoPtr<IMaster, AutoInterface>
{
public:
MasterInterface() : AutoPtr<IMaster, AutoInterface>(fb_get_master_interface())
{ }
};
// Generic plugins interface
class PluginInterface : public AutoPtr<IPlugin, AutoInterface>
{
@ -183,6 +193,19 @@ public:
};
// Control timer interface
class TimerInterface : public AutoPtr<ITimerControl, AutoInterface>
{
public:
TimerInterface() : AutoPtr<ITimerControl, AutoInterface>(NULL)
{
IMaster* mi = fb_get_master_interface();
reset(mi->getTimerControl());
mi->release();
}
};
// When process exits, dynamically loaded modules (for us plugin modules)
// are unloaded first. As the result all global variables in plugin are already destroyed
// when yvalve is starting fb_shutdown(). This causes almost unavoidable segfault.
@ -197,21 +220,18 @@ class UnloadDetectorHelper : public StdIface<IModuleCleanup, FB_MODULE_CLEANUP_V
{
public:
UnloadDetectorHelper(MemoryPool&)
: flagOsUnload(true)
: flagOsUnload(true), cleanup(NULL)
{ }
~UnloadDetectorHelper()
{
if (flagOsUnload)
{
fb_shutdown(5000, fb_shutrsn_exit_called);
}
flagOsUnload = false;
}
PluginInterface()->resetModuleCleanup(this);
void FB_CARG doClean()
{
flagOsUnload = false;
fb_shutdown(5000, fb_shutrsn_exit_called);
doClean();
}
}
int FB_CARG release()
@ -230,8 +250,24 @@ public:
return !flagOsUnload;
}
protected:
void setCleanup(FPTR_VOID c)
{
cleanup = c;
}
private:
bool flagOsUnload;
FPTR_VOID cleanup;
void FB_CARG doClean()
{
flagOsUnload = false;
if (cleanup)
{
cleanup();
cleanup = NULL;
}
}
};
typedef GlobalPtr<UnloadDetectorHelper, InstanceControl::PRIORITY_DETECT_UNLOAD> UnloadDetector;

View File

@ -167,6 +167,7 @@ private:
static pthread_mutexattr_t attr;
#ifdef DEV_BUILD
const char* reason;
int lockCount;
#endif
private:
@ -174,6 +175,7 @@ private:
{
#ifdef DEV_BUILD
reason = NULL;
lockCount = 0;
#endif
int rc = pthread_mutex_init(&mlock, &attr);
if (rc)
@ -186,6 +188,7 @@ public:
~Mutex()
{
fb_assert(lockCount == 0);
int rc = pthread_mutex_destroy(&mlock);
if (rc)
system_call_failed::raise("pthread_mutex_destroy", rc);
@ -198,6 +201,7 @@ public:
if (rc)
system_call_failed::raise("pthread_mutex_lock", rc);
reason = aReason;
++lockCount;
}
#endif
@ -208,6 +212,7 @@ public:
system_call_failed::raise("pthread_mutex_lock", rc);
#ifdef DEV_BUILD
reason = "<..unspecified..>";
++lockCount;
#endif
}
@ -218,14 +223,43 @@ public:
return false;
if (rc)
system_call_failed::raise("pthread_mutex_trylock", rc);
#ifdef DEV_BUILD
reason = "<..unspecified..>";
++lockCount;
#endif
return true;
}
void leave()
{
fb_assert(lockCount > 0);
#ifdef DEV_BUILD
--lockCount;
#endif
int rc = pthread_mutex_unlock(&mlock);
if (rc)
{
#ifdef DEV_BUILD
++lockCount;
#endif
system_call_failed::raise("pthread_mutex_unlock", rc);
}
}
void assertLocked()
{
#ifdef DEV_BUILD
// first of all try to enter the mutex
// this will help to make sure it's not locked by other thread
if (!tryEnter())
{
fb_assert(false);
}
// make sure mutex was already locked prior assertLocked
fb_assert(lockCount > 1);
// leave to release lock, done by us in tryEnter
leave();
#endif
}
public:

View File

@ -43,6 +43,7 @@ namespace os_utils
void createLockDirectory(const char* pathname);
int openCreateSharedFile(const char* pathname, int flags);
bool touchFile(const char* pathname);
} // namespace os_utils

View File

@ -198,4 +198,23 @@ int openCreateSharedFile(const char* pathname, int flags)
return fd;
}
// set file's last access and modification time to current time
bool touchFile(const char* pathname)
{
#ifdef HAVE_UTIME_H
while (utime(pathname, NULL) < 0)
{
if (SYSCALL_INTERRUPTED(errno))
{
continue;
}
return false;
}
return true;
#else
return false;
#endif
}
} // namespace os_utils

View File

@ -241,4 +241,27 @@ int openCreateSharedFile(const char* pathname, int flags)
return ::open(pathname, flags | O_RDWR | O_CREAT, S_IREAD | S_IWRITE);
}
// set file's last access and modification time to current time
bool touchFile(const char* pathname)
{
FILETIME ft;
SYSTEMTIME st;
HANDLE hFile = CreateFile(pathname,
GENERIC_READ | FILE_WRITE_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE,
ISC_get_security_desc(),
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (hFile == INVALID_HANDLE_VALUE)
return false;
GetSystemTime(&st);
const bool ret = SystemTimeToFileTime(&st, &ft) && SetFileTime(hFile, NULL, &ft, &ft);
CloseHandle(hFile);
return ret;
}
} // namespace os_utils

View File

@ -704,7 +704,7 @@ public:
}
virtual int FB_CARG upgradeInterface(Interface* toUpgrade, int desiredVersion,
void* missingFunctionClass)
void* missingFunctionClass)
{
fb_assert(false);
return 0;
@ -717,6 +717,12 @@ public:
buf[len] = 0;
return buf;
}
virtual ITimerControl* getTimerControl()
{
fb_assert(false);
return NULL;
}
};

View File

@ -62,10 +62,6 @@
#ifndef FIREBIRD_PLUGIN_API_H
#define FIREBIRD_PLUGIN_API_H
#ifdef __GNUC__
# pragma GCC system_header // disable warning about non-existent virtual destructor
#endif
#include "Interface.h"
#define FB_PLUGIN_ENTRY_POINT firebird_plugin
@ -116,7 +112,7 @@ class IFirebirdConf : public Interface
{
public:
// Get integer key by it's name
// Value <0 means name is invalid
// Value ~0 means name is invalid
// Keys are stable: one can use once obtained key in other instances of this interface
virtual unsigned int FB_CARG getKey(const char* name) = 0;
// Use to access integer and boolean values
@ -171,7 +167,11 @@ public:
virtual void FB_CARG registerPlugin(unsigned int interfaceType, const char* defaultName,
PluginsFactory* factory) = 0;
// Sets cleanup for plugin module
// Pay attention - this should be called at plugin-regsiter time!
// Only at this moment manager knows, which module sets his cleanup
virtual void FB_CARG setModuleCleanup(IModuleCleanup* cleanup) = 0;
// Remove registered before cleanup routine
virtual void FB_CARG resetModuleCleanup(IModuleCleanup* cleanup) = 0;
// Main function called to access plugins registered in plugins manager
// Has front-end in GetPlugins.h - template GetPlugins
// In namesList parameter comma or space separated list of names of configured plugins is passed
@ -202,6 +202,8 @@ namespace PluginType {
static const unsigned int AuthUserManagement = 13;
static const unsigned int ExternalEngine = 14;
static const unsigned int Trace = 15;
static const unsigned int MaxType = 16; // keep in sync please
};
} // namespace Firebird

View File

@ -29,6 +29,8 @@
#ifndef FB_INCLUDE_INTERFACE
#define FB_INCLUDE_INTERFACE
#include "types_pub.h"
#ifdef __GNUC__
# pragma GCC system_header // disable warning about non-existent virtual destructor
#endif
@ -65,6 +67,7 @@ public:
#define FB_STATUS_VERSION (FB_INTERFACE_VERSION + 5)
class IPlugin;
class ITimerControl;
class IMaster : public Interface
{
@ -76,6 +79,7 @@ public:
virtual IPlugin* FB_CARG getPluginInterface() = 0;
virtual int FB_CARG upgradeInterface(Interface* toUpgrade, int desiredVersion, void* missingFunctionClass) = 0;
virtual const char* FB_CARG circularAlloc(const char* s, size_t len, intptr_t thr) = 0;
virtual ITimerControl* FB_CARG getTimerControl() = 0;
};
} // namespace Firebird

61
src/include/Timer.h Normal file
View File

@ -0,0 +1,61 @@
/*
* PROGRAM: Firebird interface.
* MODULE: Timer.h
* DESCRIPTION: Timer interface defnition.
*
* The contents of this file are subject to the Initial
* Developer's Public License Version 1.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
*
* Software distributed under the License is distributed AS IS,
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the License for the specific language governing rights
* and limitations under the License.
*
* The Original Code was created by Alex Peshkov
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2011 Alex Peshkov <peshkoff at mail.ru>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
*
*/
#ifndef FIREBIRD_TIMER_H
#define FIREBIRD_TIMER_H
#include "Interface.h"
namespace Firebird {
// Identifies particular timer.
// Callback function is invoked when timer fires.
class ITimer : public Interface
{
public:
virtual void FB_CARG handler() = 0;
};
#define FB_I_TIMER_VERSION (FB_INTERFACE_VERSION + 1)
typedef ISC_INT64 TimerDelay;
// Interface to set timer for particular time
class ITimerControl : public Interface
{
public:
// Set timer
virtual void FB_CARG start(ITimer* timer, TimerDelay microSeconds) = 0;
// Stop timer
virtual void FB_CARG stop(ITimer* timer) = 0;
};
#define FB_I_TIMER_CONTROL_VERSION (FB_INTERFACE_VERSION + 2)
} // namespace Firebird
#endif // FIREBIRD_TIMER_H

View File

@ -72,11 +72,6 @@ Manager::~Manager()
}
}
void Manager::init()
{
fb_shutdown_callback(0, shutdown, fb_shut_preproviders, 0);
}
void Manager::addProvider(Provider* provider)
{
for (const Provider* prv = m_providers; prv; prv = prv->m_next)
@ -113,7 +108,6 @@ Connection* Manager::getConnection(thread_db* tdbb, const string& dataSource,
Database::CheckoutLockGuard guard(tdbb->getDatabase(), m_mutex);
if (!m_initialized)
{
init();
m_initialized = true;
}
}
@ -157,7 +151,7 @@ void Manager::jrdAttachmentEnd(thread_db* tdbb, Jrd::Attachment* att)
}
}
int Manager::shutdown(const int /*reason*/, const int /*mask*/, void* /*arg*/)
int Manager::shutdown()
{
thread_db* tdbb = JRD_get_thread_data();
for (Provider* prv = m_providers; prv; prv = prv->m_next) {

View File

@ -69,11 +69,9 @@ public:
// Notify providers when some jrd attachment is about to be released
static void jrdAttachmentEnd(Jrd::thread_db* tdbb, Jrd::Attachment* att);
static int shutdown();
private:
static void init();
static int shutdown(const int reason, const int mask, void* arg);
static Firebird::GlobalPtr<Manager> manager;
static Firebird::Mutex m_mutex;
static Provider* m_providers;

View File

@ -6514,6 +6514,9 @@ static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM arg)
{
ThreadContextHolder tdbb;
// Shutdown external datasets manager
EDS::Manager::shutdown();
MutexLockGuard guard(databases_mutex);
cancel_attachments(tdbb);

View File

@ -59,6 +59,7 @@ using namespace Firebird;
namespace Jrd {
static const TimerDelay TOUCH_INTERVAL = 60 * 60; // in seconds, one hour should be enough
void checkFileError(const char* filename, const char* operation, ISC_STATUS iscError)
{
@ -120,11 +121,15 @@ ConfigStorage::ConfigStorage()
StorageGuard guard(this);
checkFile();
timer.start(sh_mem_header->cfg_file_name);
++sh_mem_header->cnt_uses;
}
ConfigStorage::~ConfigStorage()
{
timer.stop();
::close(m_cfg_file);
m_cfg_file = -1;
@ -545,4 +550,21 @@ bool ConfigStorage::getItemLength(ITEM& tag, ULONG& len)
return true;
}
void FB_CARG ConfigStorage::TouchFile::handler()
{
os_utils::touchFile(fileName);
TimerInterface()->start(this, TOUCH_INTERVAL * 1000 * 1000);
}
void ConfigStorage::TouchFile::start(const char* fName)
{
fileName = fName;
TimerInterface()->start(this, TOUCH_INTERVAL * 1000 * 1000);
}
void ConfigStorage::TouchFile::stop()
{
TimerInterface()->stop(this);
}
} // namespace Jrd

View File

@ -67,6 +67,18 @@ private:
bool initialize(bool);
void checkFile();
void touchFile();
class TouchFile : public Firebird::StackIface<Firebird::ITimer, FB_I_TIMER_VERSION>
{
public:
void FB_CARG handler();
void start(const char* fName);
void stop();
private:
const char* fileName;
};
TouchFile timer;
void checkDirty()
{

View File

@ -804,12 +804,13 @@ public:
class ExternalEngineFactoryImpl : public SimpleFactory<Engine>
{
} factory;
static Firebird::UnloadDetector unloadDetector;
extern "C" void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master)
{
IPlugin* plugin = master->getPluginInterface();
plugin->registerPlugin(PluginType::ExternalEngine, "UDR", &factory);
plugin->setModuleCleanup(&unloadDetector);
plugin->release();
libraryName->assign("fbclient");

View File

@ -28,6 +28,11 @@
#include "firebird.h"
#include "Interface.h"
#include "Timer.h"
#include <sys/time.h>
#include "../yvalve/MasterImplementation.h"
#include "../common/classes/ImplementHelper.h"
#include "../common/classes/init.h"
#include "../common/StatusHolder.h"
@ -35,7 +40,10 @@
#include "../common/classes/GenericMap.h"
#include "../common/classes/fb_pair.h"
#include "../common/classes/rwlock.h"
#include "../common/classes/semaphore.h"
#include "../common/isc_proto.h"
#include "../common/ThreadStart.h"
#include "../jrd/ibase.h"
namespace Firebird {
@ -46,6 +54,7 @@ public:
IPlugin* FB_CARG getPluginInterface();
int FB_CARG upgradeInterface(Interface* toUpgrade, int desiredVersion, void* missingFunctionClass);
const char* FB_CARG circularAlloc(const char* s, size_t len, intptr_t thr);
ITimerControl* FB_CARG getTimerControl();
};
//
@ -335,6 +344,191 @@ const char* FB_CARG MasterImplementation::circularAlloc(const char* s, size_t le
} // namespace Firebird
//
// timer
//
namespace Firebird {
namespace {
GlobalPtr<Mutex> timerAccess;
GlobalPtr<Semaphore> timerWakeup;
bool stopThread = false;
Thread::Handle timerThreadHandle = 0;
struct TimerEntry
{
TimerDelay fireTime;
ITimer* timer;
static const TimerDelay generate(const void* /*sender*/, const TimerEntry& item) { return item.fireTime; }
static THREAD_ENTRY_DECLARE timeThread(THREAD_ENTRY_PARAM);
static void init()
{
Thread::start(timeThread, 0, 0, &timerThreadHandle);
}
static void cleanup(void);
};
typedef SortedArray<TimerEntry, InlineStorage<TimerEntry, 64>, TimerDelay, TimerEntry> TimerQueue;
GlobalPtr<TimerQueue> timerQueue;
InitMutex<TimerEntry> timerHolder;
void TimerEntry::cleanup(void)
{
MutexLockGuard guard(timerAccess);
stopThread = true;
timerWakeup->release();
Thread::waitForCompletion(timerThreadHandle);
while (timerQueue->hasData())
{
TimerEntry& e(timerQueue->operator[](timerQueue->getCount() - 1));
e.timer->release();
timerQueue->remove(&e);
}
}
TimerDelay curTime()
{
struct timeval cur_time;
struct timezone tzUnused;
if (gettimeofday(&cur_time, &tzUnused) != 0)
{
system_call_failed::raise("gettimeofday");
}
TimerDelay microSeconds = ((TimerDelay) cur_time.tv_sec) * 1000000 + cur_time.tv_usec;
return microSeconds;
}
TimerEntry* getTimer(ITimer* timer)
{
timerAccess->assertLocked();
for (unsigned int i = 0; i < timerQueue->getCount(); ++i)
{
TimerEntry& e(timerQueue->operator[](i));
if (e.timer == timer)
{
return &e;
}
}
return NULL;
}
THREAD_ENTRY_DECLARE TimerEntry::timeThread(THREAD_ENTRY_PARAM)
{
while (!stopThread)
{
TimerDelay microSeconds = 0;
{
MutexLockGuard guard(timerAccess);
const TimerDelay cur = curTime();
while (timerQueue->getCount() > 0)
{
TimerEntry e(timerQueue->operator[](0));
if (e.fireTime <= cur)
{
timerQueue->remove((size_t)0);
e.timer->handler();
e.timer->release();
}
else
{
microSeconds = e.fireTime - cur;
break;
}
}
}
if (microSeconds)
{
timerWakeup->tryEnter(0, microSeconds / 1000);
}
else
{
timerWakeup->enter();
}
}
return 0;
}
} // namespace
class TimerImplementation : public StackIface<ITimerControl, FB_I_TIMER_CONTROL_VERSION>
{
public:
void FB_CARG start(ITimer* timer, TimerDelay microSeconds)
{
MutexLockGuard guard(timerAccess);
timerHolder.init();
if (stopThread)
{
// ignore an attempt to start timer - anyway thread to make it fire is down
return;
}
TimerEntry* curTimer = getTimer(timer);
if (!curTimer)
{
TimerEntry newTimer;
newTimer.timer = timer;
newTimer.fireTime = curTime() + microSeconds;
timerQueue->add(newTimer);
timer->addRef();
}
else
{
curTimer->fireTime = curTime() + microSeconds;
}
timerWakeup->release();
}
void FB_CARG stop(ITimer* timer)
{
MutexLockGuard guard(timerAccess);
TimerEntry* curTimer = getTimer(timer);
if (curTimer)
{
curTimer->timer->release();
timerQueue->remove(curTimer);
}
}
};
ITimerControl* FB_CARG MasterImplementation::getTimerControl()
{
static Static<TimerImplementation> timer;
timer->addRef();
return &timer;
}
void shutdownTimers()
{
timerHolder.cleanup();
}
} // namespace Firebird
//
// get master
//

View File

@ -30,6 +30,7 @@
#include "../common/isc_proto.h"
#include "../common/classes/fb_string.h"
#include "../common/classes/init.h"
#include "../common/classes/semaphore.h"
#include "../common/config/config.h"
#include "../common/config/config_file.h"
#include "../common/utils_proto.h"
@ -295,6 +296,22 @@ namespace
cleanup = c;
}
void resetCleanup(IModuleCleanup* c)
{
if (cleanup == c)
{
cleanup = 0;
}
else if (next)
{
next->resetCleanup(c);
}
else
{
gds__log("Failed to reset cleanup %p\n", c);
}
}
private:
~PluginModule()
{
@ -323,6 +340,33 @@ namespace
PathName name;
};
struct CountByType
{
int counter;
Semaphore* waitsOn;
CountByType()
: counter(0), waitsOn(NULL)
{ }
};
struct CountByTypeArray
{
CountByTypeArray(MemoryPool&)
{}
CountByType values[PluginType::MaxType];
CountByType& get(unsigned int t)
{
fb_assert(t < PluginType::MaxType);
return values[t];
}
};
GlobalPtr<CountByTypeArray> byTypeCounters;
PluginModule* builtin = NULL;
class ConfiguredPlugin : public RefCounted, public GlobalStorage
{
public:
@ -340,6 +384,10 @@ namespace
confName = p->value.ToPathName();
}
}
if (module != builtin)
{
byTypeCounters->get(module->getPlugin(regPlugin).type).counter++;
}
#ifdef DEBUG_PLUGINS
RegisteredPlugin& r(module->getPlugin(regPlugin));
fprintf(stderr, " ConfiguredPlugin %s module %s registered as %s type %d order %d\n",
@ -465,7 +513,7 @@ namespace
{
public:
explicit PluginsMap(MemoryPool& p)
: GenericMap<Pair<Left<MapKey, ConfiguredPlugin*> > >(p)
: GenericMap<Pair<Left<MapKey, ConfiguredPlugin*> > >(p), wakeIt(NULL)
{
}
@ -484,6 +532,7 @@ namespace
}
Mutex mutex;
Semaphore* wakeIt;
};
GlobalPtr<PluginsMap> plugins;
@ -494,6 +543,16 @@ namespace
{
plugins->remove(MapKey(module->getPlugin(regPlugin).type, plugName));
}
fb_assert(!plugins->wakeIt);
if (module != builtin)
{
unsigned int type = module->getPlugin(regPlugin).type;
if (--(byTypeCounters->get(type).counter) == 0)
{
plugins->wakeIt = byTypeCounters->get(type).waitsOn;
}
}
#ifdef DEBUG_PLUGINS
fprintf(stderr, "~ConfiguredPlugin %s type %d\n", plugName.c_str(), module->getPlugin(regPlugin).type);
#endif
@ -501,7 +560,6 @@ namespace
PluginModule* modules = NULL;
PluginModule* builtin = NULL;
PluginModule* current = NULL;
PluginModule::PluginModule(ModuleLoader::Module* pmodule, const PathName& pname)
@ -773,6 +831,12 @@ void FB_CARG PluginManager::setModuleCleanup(IModuleCleanup* cleanup)
current->setCleanup(cleanup);
}
void FB_CARG PluginManager::resetModuleCleanup(IModuleCleanup* cleanup)
{
MutexLockGuard g(plugins->mutex);
modules->resetCleanup(cleanup);
}
IPluginSet* FB_CARG PluginManager::getPlugins(unsigned int interfaceType, const char* namesList,
int desiredVersion, void* missingFunctionClass,
@ -797,9 +861,15 @@ void FB_CARG PluginManager::releasePlugin(Plugin* plugin)
if (parent)
{
parent->release();
if (plugins->wakeIt)
{
plugins->wakeIt->release();
plugins->wakeIt = NULL;
}
}
}
else {
else
{
plugin->owner(parent);
}
}
@ -816,4 +886,35 @@ void PluginManager::shutdown()
flShutdown = true;
}
void PluginManager::waitForType(unsigned int typeThatMustGoAway)
{
fb_assert(typeThatMustGoAway < PluginType::MaxType);
Semaphore sem;
Semaphore* semPtr = NULL;
{ // guard scope
MutexLockGuard g(plugins->mutex);
if (byTypeCounters->get(typeThatMustGoAway).counter > 0)
{
fb_assert(!byTypeCounters->get(typeThatMustGoAway).waitsOn);
byTypeCounters->get(typeThatMustGoAway).waitsOn = semPtr = &sem;
#ifdef DEBUG_PLUGINS
fprintf(stderr, "PluginManager::waitForType %d\n", typeThatMustGoAway);
#endif
}
#ifdef DEBUG_PLUGINS
else
{
fprintf(stderr, "PluginManager: type %d is already gone\n", typeThatMustGoAway);
}
#endif
}
if (semPtr)
{
semPtr->enter();
}
}
} // namespace Firebird

View File

@ -49,10 +49,12 @@ public:
IConfig* FB_CARG getConfig(const char* filename);
void FB_CARG releasePlugin(Plugin* plugin);
void FB_CARG setModuleCleanup(IModuleCleanup* cleanup);
void FB_CARG resetModuleCleanup(IModuleCleanup* cleanup);
PluginManager();
static void shutdown();
static void waitForType(unsigned int typeThatMustGoAway);
};
} // namespace Firebird

File diff suppressed because it is too large Load Diff