core/Database.cpp
author Kyle Sanderson <kyle.leet@gmail.com>
Thu Jul 18 20:34:43 2013 -0400 (2013-07-18)
changeset 3955 763d59fe6d77
parent 3872 4c93f74cae51
child 4027 d2c15108b468
permissions -rw-r--r--
Added many missing handle security checks (bug 5595, r=psychonic).
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "Database.h"
    33 #include "sourcemod.h"
    34 #include "sm_stringutil.h"
    35 #include "Logger.h"
    36 #include <stdlib.h>
    37 #include <IThreader.h>
    38 #include "logic_bridge.h"
    39 
    40 #define DBPARSE_LEVEL_NONE		0
    41 #define DBPARSE_LEVEL_MAIN		1
    42 #define DBPARSE_LEVEL_DATABASE	2
    43 
    44 DBManager g_DBMan;
    45 static bool s_OneTimeThreaderErrorMsg = false;
    46 
    47 DBManager::DBManager() 
    48 : m_ParseLevel(0), m_ParseState(0), m_pDefault(NULL)
    49 {
    50 }
    51 
    52 void DBManager::OnSourceModAllInitialized()
    53 {
    54 	HandleAccess sec;
    55 
    56 	handlesys->InitAccessDefaults(NULL, &sec);
    57 	sec.access[HandleAccess_Delete] |= HANDLE_RESTRICT_IDENTITY;
    58 	sec.access[HandleAccess_Clone] |= HANDLE_RESTRICT_IDENTITY;
    59 	
    60 	m_DriverType = handlesys->CreateType("IDriver", this, 0, NULL, &sec, g_pCoreIdent, NULL);
    61 	m_DatabaseType = handlesys->CreateType("IDatabase", this, 0, NULL, NULL, g_pCoreIdent, NULL);
    62 
    63 	sharesys->AddInterface(NULL, this);
    64 
    65 	g_SourceMod.BuildPath(Path_SM, m_Filename, sizeof(m_Filename), "configs/databases.cfg");
    66 
    67 	m_pConfigLock = g_pThreader->MakeMutex();
    68 	m_pThinkLock = g_pThreader->MakeMutex();
    69 	m_pQueueLock = g_pThreader->MakeMutex();
    70 
    71 	scripts->AddPluginsListener(this);
    72 }
    73 
    74 void DBManager::OnSourceModLevelChange(const char *mapName)
    75 {
    76 	SMCError err;
    77 	SMCStates states = {0, 0};
    78 
    79 	/* We lock and don't give up the lock until we're done.
    80 	 * This way the thread's search won't be searching through a
    81 	 * potentially empty/corrupt list, which would be very bad.
    82 	 */
    83 	m_pConfigLock->Lock();
    84 	if ((err = textparsers->ParseFile_SMC(m_Filename, this, &states)) != SMCError_Okay)
    85 	{
    86 		g_Logger.LogError("[SM] Detected parse error(s) in file \"%s\"", m_Filename);
    87 		if (err != SMCError_Custom)
    88 		{
    89 			const char *txt = textparsers->GetSMCErrorString(err);
    90 			g_Logger.LogError("[SM] Line %d: %s", states.line, txt);
    91 		}
    92 	}
    93 	m_pConfigLock->Unlock();
    94 }
    95 
    96 void DBManager::OnSourceModShutdown()
    97 {
    98 	KillWorkerThread();
    99 	scripts->RemovePluginsListener(this);
   100 	m_pConfigLock->DestroyThis();
   101 	m_pThinkLock->DestroyThis();
   102 	m_pQueueLock->DestroyThis();
   103 	handlesys->RemoveType(m_DatabaseType, g_pCoreIdent);
   104 	handlesys->RemoveType(m_DriverType, g_pCoreIdent);
   105 	ClearConfigs();
   106 }
   107 
   108 unsigned int DBManager::GetInterfaceVersion()
   109 {
   110 	return SMINTERFACE_DBI_VERSION;
   111 }
   112 
   113 const char *DBManager::GetInterfaceName()
   114 {
   115 	return SMINTERFACE_DBI_NAME;
   116 }
   117 
   118 void DBManager::OnHandleDestroy(HandleType_t type, void *object)
   119 {
   120 	if (type == m_DriverType)
   121 	{
   122 		/* Ignore */
   123 		return;
   124 	}
   125 
   126 	if (handlesys->TypeCheck(type, m_DatabaseType))
   127 	{
   128 		IDatabase *pdb = (IDatabase *)object;
   129 		pdb->Close();
   130 	}
   131 }
   132 
   133 void DBManager::ReadSMC_ParseStart()
   134 {
   135 	ClearConfigs();
   136 	m_ParseLevel = 0;
   137 	m_ParseState = DBPARSE_LEVEL_NONE;
   138 	m_DefDriver.clear();
   139 }
   140 
   141 void DBManager::ClearConfigs()
   142 {
   143 	List<ConfDbInfo *>::iterator iter;
   144 	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
   145 		delete (*iter);
   146 	m_confs.clear();
   147 }
   148 
   149 ConfDbInfo s_CurInfo;
   150 SMCResult DBManager::ReadSMC_NewSection(const SMCStates *states, const char *name)
   151 {
   152 	if (m_ParseLevel)
   153 	{
   154 		m_ParseLevel++;
   155 		return SMCResult_Continue;
   156 	}
   157 
   158 	if (m_ParseState == DBPARSE_LEVEL_NONE)
   159 	{
   160 		if (strcmp(name, "Databases") == 0)
   161 		{
   162 			m_ParseState = DBPARSE_LEVEL_MAIN;
   163 		} else {
   164 			m_ParseLevel++;
   165 		}
   166 	} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
   167 		s_CurInfo = ConfDbInfo();
   168 		s_CurInfo.name = name;
   169 		m_ParseState = DBPARSE_LEVEL_DATABASE;
   170 	} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
   171 		m_ParseLevel++;
   172 	}
   173 
   174 	return SMCResult_Continue;
   175 }
   176 
   177 SMCResult DBManager::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   178 {
   179 	if (m_ParseLevel)
   180 	{
   181 		return SMCResult_Continue;
   182 	}
   183 
   184 	if (m_ParseState == DBPARSE_LEVEL_MAIN)
   185 	{
   186 		if (strcmp(key, "driver_default") == 0)
   187 		{
   188 			m_DefDriver.assign(value);
   189 		}
   190 	} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
   191 		if (strcmp(key, "driver") == 0)
   192 		{
   193 			if (strcmp(value, "default") != 0)
   194 			{
   195 				s_CurInfo.driver = value;
   196 			}
   197 		} else if (strcmp(key, "database") == 0) {
   198 			s_CurInfo.database = value;
   199 		} else if (strcmp(key, "host") == 0) {
   200 			s_CurInfo.host = value;
   201 		} else if (strcmp(key, "user") == 0) {
   202 			s_CurInfo.user = value;
   203 		} else if (strcmp(key, "pass") == 0) {
   204 			s_CurInfo.pass = value;
   205 		} else if (strcmp(key, "timeout") == 0) {
   206 			s_CurInfo.info.maxTimeout = atoi(value);
   207 		} else if (strcmp(key, "port") == 0) {
   208 			s_CurInfo.info.port = atoi(value);
   209 		}
   210 	}
   211 
   212 	return SMCResult_Continue;
   213 }
   214 
   215 SMCResult DBManager::ReadSMC_LeavingSection(const SMCStates *states)
   216 {
   217 	if (m_ParseLevel)
   218 	{
   219 		m_ParseLevel--;
   220 		return SMCResult_Continue;
   221 	}
   222 
   223 	if (m_ParseState == DBPARSE_LEVEL_DATABASE)
   224 	{
   225 		ConfDbInfo *cdb = new ConfDbInfo();
   226 
   227 		cdb->name = s_CurInfo.name;
   228 		cdb->driver = s_CurInfo.driver;
   229 		cdb->host = s_CurInfo.host;
   230 		cdb->user = s_CurInfo.user;
   231 		cdb->pass = s_CurInfo.pass;
   232 		cdb->database = s_CurInfo.database;
   233 		cdb->realDriver = s_CurInfo.realDriver;
   234 		cdb->info.maxTimeout = s_CurInfo.info.maxTimeout;
   235 		cdb->info.port = s_CurInfo.info.port;
   236 
   237 		cdb->info.driver = cdb->driver.c_str();
   238 		cdb->info.database = cdb->database.c_str();
   239 		cdb->info.host = cdb->host.c_str();
   240 		cdb->info.user = cdb->user.c_str();
   241 		cdb->info.pass = cdb->pass.c_str();
   242 		
   243 		/* Save it.. */
   244 		m_confs.push_back(cdb);
   245 
   246 		/* Go up one level */
   247 		m_ParseState = DBPARSE_LEVEL_MAIN;
   248 	} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
   249 		m_ParseState = DBPARSE_LEVEL_NONE;
   250 		return SMCResult_Halt;
   251 	}
   252 
   253 	return SMCResult_Continue;
   254 }
   255 
   256 void DBManager::ReadSMC_ParseEnd(bool halted, bool failed)
   257 {
   258 }
   259 
   260 bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength)
   261 {
   262 	ConfDbInfo *pInfo = GetDatabaseConf(name);
   263 
   264 	if (!pInfo)
   265 	{
   266 		if (pdr)
   267 		{
   268 			*pdr = NULL;
   269 		}
   270 		*pdb = NULL;
   271 		UTIL_Format(error, maxlength, "Configuration \"%s\" not found", name);
   272 		return false;
   273 	}
   274 
   275 	const char *dname = pInfo->info.driver;
   276 	if (!pInfo->realDriver)
   277 	{
   278 		/* Try to assign a real driver pointer */
   279 		if (pInfo->info.driver[0] == '\0')
   280 		{
   281 			if (!m_pDefault && m_DefDriver.size() > 0)
   282 			{
   283 				m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
   284 			}
   285 			dname = m_DefDriver.size() ? m_DefDriver.c_str() : "default";
   286 			pInfo->realDriver = m_pDefault;
   287 		} else {
   288 			pInfo->realDriver = FindOrLoadDriver(pInfo->info.driver);
   289 		}
   290 	}
   291 
   292 	if (pInfo->realDriver)
   293 	{
   294 		if (pdr)
   295 		{
   296 			*pdr = pInfo->realDriver;
   297 		}
   298 		*pdb = pInfo->realDriver->Connect(&pInfo->info, persistent, error, maxlength);
   299 		return (*pdb != NULL);
   300 	}
   301 
   302 	if (pdr)
   303 	{
   304 		*pdr = NULL;
   305 	}
   306 	*pdb = NULL;
   307 
   308 	UTIL_Format(error, maxlength, "Driver \"%s\" not found", dname);
   309 
   310 	return false;
   311 }
   312 
   313 void DBManager::AddDriver(IDBDriver *pDriver)
   314 {
   315 	 /* Let's kill the worker.  Join the thread and let the queries flush.
   316 	  * This is kind of stupid but we just want to unload safely.
   317 	  * Rather than recreate the worker, we'll wait until someone throws 
   318 	  * another query through.
   319 	  */
   320 	KillWorkerThread();
   321 
   322 	 m_drivers.push_back(pDriver);
   323 }
   324 
   325 void DBManager::RemoveDriver(IDBDriver *pDriver)
   326 {
   327 	/* Again, we're forced to kill the worker.  How rude!
   328 	 * Doing this flushes the queue, and thus we don't need to 
   329 	 * clean anything else.  
   330 	 */
   331 	KillWorkerThread();
   332 
   333 	for (size_t i=0; i<m_drivers.size(); i++)
   334 	{
   335 		if (m_drivers[i] == pDriver)
   336 		{
   337 			m_drivers.erase(m_drivers.iterAt(i));
   338 			break;
   339 		}
   340 	}
   341 
   342 	/* Make sure NOTHING references this! */
   343 	List<ConfDbInfo *>::iterator iter;
   344 	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
   345 	{
   346 		ConfDbInfo &db = *(*iter);
   347 		if (db.realDriver == pDriver)
   348 		{
   349 			db.realDriver = NULL;
   350 		}
   351 	}
   352 
   353 	/* Now that the driver is gone, we have to test the think queue.
   354 	 * Whatever happens therein is up to the db op!
   355 	 */
   356 	Queue<IDBThreadOperation *>::iterator qiter = m_ThinkQueue.begin();
   357 	Queue<IDBThreadOperation *> templist;
   358 	while (qiter != m_ThinkQueue.end())
   359 	{
   360 		IDBThreadOperation *op = (*qiter);
   361 		if (op->GetDriver() == pDriver)
   362 		{
   363 			templist.push(op);
   364 			qiter = m_ThinkQueue.erase(qiter);
   365 		} else {
   366 			qiter++;
   367 		}
   368 	}
   369 
   370 	for (qiter = templist.begin();
   371 		 qiter != templist.end();
   372 		 qiter++)
   373 	{
   374 		IDBThreadOperation *op = (*qiter);
   375 		op->CancelThinkPart();
   376 		op->Destroy();
   377 	}
   378 }
   379 
   380 IDBDriver *DBManager::GetDefaultDriver()
   381 {
   382 	if (!m_pDefault && m_DefDriver.size() > 0)
   383 	{
   384 		m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
   385 	}
   386 
   387 	return m_pDefault;
   388 }
   389 
   390 Handle_t DBManager::CreateHandle(DBHandleType dtype, void *ptr, IdentityToken_t *pToken)
   391 {
   392 	HandleType_t type = 0;
   393 
   394 	if (dtype == DBHandle_Driver)
   395 	{
   396 		type = m_DriverType;
   397 	} else if (dtype == DBHandle_Database) {
   398 		type = m_DatabaseType;
   399 	} else {
   400 		return BAD_HANDLE;
   401 	}
   402 
   403 	return handlesys->CreateHandle(type, ptr, pToken, g_pCoreIdent, NULL);
   404 }
   405 
   406 HandleError DBManager::ReadHandle(Handle_t hndl, DBHandleType dtype, void **ptr)
   407 {
   408 	HandleType_t type = 0;
   409 
   410 	if (dtype == DBHandle_Driver)
   411 	{
   412 		type = m_DriverType;
   413 	} else if (dtype == DBHandle_Database) {
   414 		type = m_DatabaseType;
   415 	} else {
   416 		return HandleError_Type;
   417 	}
   418 
   419 	HandleSecurity sec(NULL, g_pCoreIdent);
   420 
   421 	return handlesys->ReadHandle(hndl, type, &sec, ptr);
   422 }
   423 
   424 HandleError DBManager::ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token)
   425 {
   426 	HandleSecurity sec(token, g_pCoreIdent);
   427 	return handlesys->FreeHandle(hndl, &sec);
   428 }
   429 
   430 unsigned int DBManager::GetDriverCount()
   431 {
   432 	return (unsigned int)m_drivers.size();
   433 }
   434 
   435 IDBDriver *DBManager::GetDriver(unsigned int index)
   436 {
   437 	if (index >= m_drivers.size())
   438 	{
   439 		return NULL;
   440 	}
   441 
   442 	return m_drivers[index];
   443 }
   444 
   445 const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
   446 {
   447 	ConfDbInfo *info = GetDatabaseConf(name);
   448 	if (!info)
   449 	{
   450 		return NULL;
   451 	}
   452 
   453 	return &info->info;
   454 }
   455 
   456 ConfDbInfo *DBManager::GetDatabaseConf(const char *name)
   457 {
   458 	List<ConfDbInfo *>::iterator iter;
   459 
   460 	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
   461 	{
   462 		ConfDbInfo &conf = *(*iter);
   463 		if (conf.name == name)
   464 		{
   465 			return &conf;
   466 		}
   467 	}
   468 
   469 	return NULL;
   470 }
   471 
   472 IDBDriver *DBManager::FindOrLoadDriver(const char *name)
   473 {
   474 	size_t last_size = m_drivers.size();
   475 	for (size_t i=0; i<last_size; i++)
   476 	{
   477 		if (strcmp(m_drivers[i]->GetIdentifier(), name) == 0)
   478 		{
   479 			return m_drivers[i];
   480 		}
   481 	}
   482 
   483 	char filename[PLATFORM_MAX_PATH];
   484 	UTIL_Format(filename, sizeof(filename), "dbi.%s.ext", name);
   485 
   486 	IExtension *pExt = extsys->LoadAutoExtension(filename);
   487 	if (!pExt || !pExt->IsLoaded() || m_drivers.size() <= last_size)
   488 	{
   489 		return NULL;
   490 	}
   491 
   492 	/* last_size is now gauranteed to be a valid index.
   493 	 * The identifier must match the name.
   494 	 */
   495 	if (strcmp(m_drivers[last_size]->GetIdentifier(), name) == 0)
   496 	{
   497 		return m_drivers[last_size];
   498 	}
   499 
   500 	return NULL;
   501 }
   502 
   503 void DBManager::KillWorkerThread()
   504 {
   505 	if (m_pWorker)
   506 	{
   507 		m_pWorker->Stop(false);
   508 		g_pThreader->DestroyWorker(m_pWorker);
   509 		m_pWorker = NULL;
   510 		s_OneTimeThreaderErrorMsg = false;
   511 	}
   512 }
   513 
   514 static IdentityToken_t *s_pAddBlock = NULL;
   515 
   516 bool DBManager::AddToThreadQueue(IDBThreadOperation *op, PrioQueueLevel prio)
   517 {
   518 	if (s_pAddBlock && op->GetOwner() == s_pAddBlock)
   519 	{
   520 		return false;
   521 	}
   522 
   523 	if (!m_pWorker)
   524 	{
   525 		m_pWorker = g_pThreader->MakeWorker(this, true);
   526 		if (!m_pWorker)
   527 		{
   528 			if (!s_OneTimeThreaderErrorMsg)
   529 			{
   530 				g_Logger.LogError("[SM] Unable to create db threader (error unknown)");
   531 				s_OneTimeThreaderErrorMsg = true;
   532 			}
   533 			return false;
   534 		}
   535 		if (!m_pWorker->Start())
   536 		{
   537 			if (!s_OneTimeThreaderErrorMsg)
   538 			{
   539 				g_Logger.LogError("[SM] Unable to start db threader (error unknown)");
   540 				s_OneTimeThreaderErrorMsg = true;
   541 			}
   542 			g_pThreader->DestroyWorker(m_pWorker);
   543 			m_pWorker = NULL;
   544 			return false;
   545 		}
   546 	}
   547 
   548 	/* Add to the queue */
   549 	{
   550 		m_pQueueLock->Lock();
   551 		Queue<IDBThreadOperation *> &queue = m_OpQueue.GetQueue(prio);
   552 		queue.push(op);
   553 		m_pQueueLock->Unlock();
   554 	}
   555 
   556 	/* Make the thread */
   557 	m_pWorker->MakeThread(this);
   558 
   559 	return true;
   560 }
   561 
   562 void DBManager::OnWorkerStart(IThreadWorker *pWorker)
   563 {
   564 	m_drSafety.clear();
   565 	for (size_t i=0; i<m_drivers.size(); i++)
   566 	{
   567 		if (m_drivers[i]->IsThreadSafe())
   568 		{
   569 			m_drSafety.push_back(m_drivers[i]->InitializeThreadSafety());
   570 		} else {
   571 			m_drSafety.push_back(false);
   572 		}
   573 	}
   574 }
   575 
   576 void DBManager::OnWorkerStop(IThreadWorker *pWorker)
   577 {
   578 	for (size_t i=0; i<m_drivers.size(); i++)
   579 	{
   580 		if (m_drSafety[i])
   581 		{
   582 			m_drivers[i]->ShutdownThreadSafety();
   583 		}
   584 	}
   585 	m_drSafety.clear();
   586 }
   587 
   588 void DBManager::RunThread(IThreadHandle *pThread)
   589 {
   590 	IDBThreadOperation *op = NULL;
   591 
   592 	/* Get something from the queue */
   593 	{
   594 		m_pQueueLock->Lock();
   595 		Queue<IDBThreadOperation *> &queue = m_OpQueue.GetLikelyQueue();
   596 		if (!queue.empty())
   597 		{
   598 			op = queue.first();
   599 			queue.pop();
   600 		}
   601 		m_pQueueLock->Unlock();
   602 	}
   603 
   604 	/* whoa.  hi.  did we get something? we should have. */
   605 	if (!op)
   606 	{
   607 		/* wtf? */
   608 		return;
   609 	}
   610 
   611 	op->RunThreadPart();
   612 
   613 	m_pThinkLock->Lock();
   614 	m_ThinkQueue.push(op);
   615 	m_pThinkLock->Unlock();
   616 }
   617 
   618 void DBManager::RunFrame()
   619 {
   620 	/* Don't bother if we're empty */
   621 	if (!m_ThinkQueue.size())
   622 	{
   623 		return;
   624 	}
   625 
   626 	/* Dump one thing per-frame so the server stays sane. */
   627 	m_pThinkLock->Lock();
   628 	IDBThreadOperation *op = m_ThinkQueue.first();
   629 	m_ThinkQueue.pop();
   630 	m_pThinkLock->Unlock();
   631 	op->RunThinkPart();
   632 	op->Destroy();
   633 }
   634 
   635 void DBManager::OnTerminate(IThreadHandle *pThread, bool cancel)
   636 {
   637 	/* Do nothing */
   638 }
   639 
   640 void DBManager::OnSourceModIdentityDropped(IdentityToken_t *pToken)
   641 {
   642 	s_pAddBlock = pToken;
   643 
   644 	/* Kill the thread so we can flush everything into the think queue... */
   645 	KillWorkerThread();
   646 
   647 	/* Run all of the think operations.
   648  	 * Unlike the driver unloading example, we'll let these calls go through, 
   649 	 * since a plugin unloading is far more normal.
   650 	 */
   651 	Queue<IDBThreadOperation *>::iterator iter = m_ThinkQueue.begin();
   652 	Queue<IDBThreadOperation *> templist;
   653 	while (iter != m_ThinkQueue.end())
   654 	{
   655 		IDBThreadOperation *op = (*iter);
   656 		if (op->GetOwner() == pToken)
   657 		{
   658 			templist.push(op);
   659 			iter = m_ThinkQueue.erase(iter);
   660 		} else {
   661 			iter++;
   662 		}
   663 	}
   664 
   665 	for (iter = templist.begin();
   666 		iter != templist.end();
   667 		iter++)
   668 	{
   669 		IDBThreadOperation *op = (*iter);
   670 		op->RunThinkPart();
   671 		op->Destroy();
   672 	}
   673 
   674 	s_pAddBlock = NULL;
   675 }
   676 
   677 void DBManager::OnPluginUnloaded(IPlugin *plugin)
   678 {
   679 	/* Kill the thread so we can flush everything into the think queue... */
   680 	KillWorkerThread();
   681 
   682 	/* Mark the plugin as being unloaded so future database calls will ignore threading... */
   683 	plugin->SetProperty("DisallowDBThreads", NULL);
   684 
   685 	/* Run all of the think operations.
   686 	 * Unlike the driver unloading example, we'll let these calls go through, 
   687 	 * since a plugin unloading is far more normal.
   688 	 */
   689 	Queue<IDBThreadOperation *>::iterator iter = m_ThinkQueue.begin();
   690 	Queue<IDBThreadOperation *> templist;
   691 	while (iter != m_ThinkQueue.end())
   692 	{
   693 		IDBThreadOperation *op = (*iter);
   694 		if (op->GetOwner() == plugin->GetIdentity())
   695 		{
   696 			templist.push(op);
   697 			iter = m_ThinkQueue.erase(iter);
   698 		} else {
   699 			iter++;
   700 		}
   701 	}
   702 
   703 	for (iter = templist.begin();
   704 		 iter != templist.end();
   705 		 iter++)
   706 	{
   707 		IDBThreadOperation *op = (*iter);
   708 		op->RunThinkPart();
   709 		op->Destroy();
   710 	}
   711 }
   712 
   713 void DBManager::LockConfig()
   714 {
   715 	m_pConfigLock->Lock();
   716 }
   717 
   718 void DBManager::UnlockConfig()
   719 {
   720 	m_pConfigLock->Unlock();
   721 }
   722 
   723 const char *DBManager::GetDefaultDriverName()
   724 {
   725 	return m_DefDriver.c_str();
   726 }
   727 
   728 void DBManager::AddDependency(IExtension *myself, IDBDriver *driver)
   729 {
   730 	extsys->AddRawDependency(myself, driver->GetIdentity(), driver);
   731 }
   732