mirror of
https://github.com/TTimo/GtkRadiant.git
synced 2024-11-10 07:11:54 +00:00
b1bfb19ecd
git-svn-id: svn://svn.icculus.org/gtkradiant/GtkRadiant/branches/ZeroRadiant.ab@186 8a3a26a2-13c4-0310-b231-cf6edde360e5
1100 lines
31 KiB
C++
1100 lines
31 KiB
C++
/*
|
|
Copyright (C) 1999-2007 id Software, Inc. and contributors.
|
|
For a list of contributors, see the accompanying CONTRIBUTORS file.
|
|
|
|
This file is part of GtkRadiant.
|
|
|
|
GtkRadiant 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.
|
|
|
|
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
// seems to be required for str.h
|
|
#include <glib.h>
|
|
|
|
#include "synapse.h"
|
|
#if defined (__linux__) || defined (__APPLE__)
|
|
#include <dirent.h>
|
|
#endif
|
|
|
|
/*
|
|
===================================================
|
|
diagnostic stuff
|
|
===================================================
|
|
*/
|
|
|
|
extern "C"
|
|
{
|
|
|
|
static PFN_SYN_PRINTF_VA g_pPrintf = NULL;
|
|
|
|
void Set_Syn_Printf(PFN_SYN_PRINTF_VA pf)
|
|
{
|
|
g_pPrintf = pf;
|
|
}
|
|
|
|
#define BUFFER_SIZE 4096
|
|
|
|
void Syn_Printf (const char *text, ...)
|
|
{
|
|
char buf[BUFFER_SIZE];
|
|
va_list args;
|
|
|
|
if (!text)
|
|
return;
|
|
|
|
if (g_pPrintf)
|
|
{
|
|
va_start (args, text);
|
|
(*g_pPrintf)(text, args);
|
|
va_end (args);
|
|
}
|
|
else
|
|
{
|
|
va_start (args, text);
|
|
vsnprintf (buf, BUFFER_SIZE, text, args);
|
|
buf[BUFFER_SIZE-1] = 0;
|
|
printf(buf);
|
|
va_end (args);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
=======================================================================
|
|
server
|
|
=======================================================================
|
|
*/
|
|
|
|
// this must be kept in sync with EAPIType
|
|
static const char* APITypeName[4] =
|
|
{
|
|
"SYN_UNKNOWN",
|
|
"SYN_PROVIDE",
|
|
"SYN_REQUIRE",
|
|
"SYN_REQUIRE_ANY"
|
|
};
|
|
|
|
CSynapseServer::CSynapseServer()
|
|
{
|
|
mpDoc = NULL;
|
|
m_api_name = NULL;
|
|
m_content = NULL;
|
|
mpFocusedNode = NULL;
|
|
}
|
|
|
|
CSynapseServer::~CSynapseServer()
|
|
{
|
|
if (m_api_name)
|
|
xmlFree(m_api_name);
|
|
if (m_content)
|
|
g_free(m_content);
|
|
Syn_Printf("TODO: free API managers\n");
|
|
}
|
|
|
|
void CSynapseServer::AddSearchPath(char* path)
|
|
{
|
|
char *pLocalPath = new char[strlen(path)+1];
|
|
strcpy(pLocalPath, path);
|
|
mSearchPaths.push_front(pLocalPath);
|
|
}
|
|
|
|
bool CSynapseServer::Initialize(const char* conf_file, PFN_SYN_PRINTF_VA pf)
|
|
{
|
|
// browse the paths to locate all potential modules
|
|
|
|
Set_Syn_Printf(pf);
|
|
|
|
if (conf_file)
|
|
{
|
|
// if a config file is specified and we fail to load it, we fail
|
|
Syn_Printf("loading synapse XML config file '%s'\n", conf_file);
|
|
mpDoc = xmlParseFile(conf_file);
|
|
if (!mpDoc)
|
|
{
|
|
Syn_Printf("'%s' invalid/not found\n", conf_file);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for (list<char *>::iterator iPath=mSearchPaths.begin(); iPath!=mSearchPaths.end(); iPath++)
|
|
{
|
|
const char* path = *iPath;
|
|
|
|
Syn_Printf("Synapse Scanning modules path: %s\n", path);
|
|
|
|
GDir* dir = g_dir_open (path, 0, NULL);
|
|
|
|
if (dir != NULL)
|
|
{
|
|
while (1)
|
|
{
|
|
const gchar* name = g_dir_read_name(dir);
|
|
if(name == NULL)
|
|
break;
|
|
|
|
// too small to be isolated in win32/ and linux/ directories..
|
|
#if defined(_WIN32)
|
|
const char* ext_so = ".dll";
|
|
#elif defined (__linux__) || defined (__APPLE__)
|
|
const char* ext_so = ".so";
|
|
#endif
|
|
const char* ext = strrchr (name, '.');
|
|
if ((ext == NULL) || (stricmp (ext, ext_so) != 0))
|
|
continue;
|
|
|
|
Str newModule;
|
|
newModule.Format("%s%s", path, name);
|
|
Syn_Printf("Found '%s'\n", newModule.GetBuffer());
|
|
EnumerateInterfaces(newModule);
|
|
}
|
|
|
|
g_dir_close(dir);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
#define FORMAT_BUFSIZE 2048
|
|
const char* CSynapseServer::FormatGetLastError()
|
|
{
|
|
static char buf[FORMAT_BUFSIZE];
|
|
FormatMessage(
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL,
|
|
GetLastError(),
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
|
buf,
|
|
FORMAT_BUFSIZE,
|
|
NULL
|
|
);
|
|
return buf;
|
|
}
|
|
|
|
void CSynapseServer::EnumerateInterfaces(Str &soname)
|
|
{
|
|
CSynapseClientSlot slot;
|
|
slot.mpDLL = LoadLibrary(soname.GetBuffer());
|
|
if (!slot.mpDLL)
|
|
{
|
|
Syn_Printf("LoadLibrary '%s' failed\n", soname.GetBuffer());
|
|
Syn_Printf(" GetLastError: %s", FormatGetLastError());
|
|
return;
|
|
}
|
|
slot.mpEnumerate = (PFN_SYNAPSE_ENUMERATEINTERFACES)GetProcAddress(slot.mpDLL, NAME_SYNAPSE_ENUMERATEINTERFACES);
|
|
if (!slot.mpEnumerate)
|
|
{
|
|
Syn_Printf("GetProcAddress('%s') failed\n", NAME_SYNAPSE_ENUMERATEINTERFACES);
|
|
Syn_Printf(" GetLastError: %s", FormatGetLastError());
|
|
return;
|
|
}
|
|
Syn_Printf("Enumerate interfaces on '%s'\n", soname.GetBuffer());
|
|
slot.mpClient = slot.mpEnumerate(SYNAPSE_VERSION, this);
|
|
if (!slot.mpClient)
|
|
{
|
|
Syn_Printf("Enumerate interfaces on '%s' returned NULL, unloading.\n", soname.GetBuffer());
|
|
if (!FreeLibrary(slot.mpDLL))
|
|
{
|
|
Syn_Printf(" FreeLibrary failed: GetLastError: '%s'\n", CSynapseServer::FormatGetLastError());
|
|
}
|
|
return;
|
|
}
|
|
slot.mFileName = soname;
|
|
mClients.push_front(slot);
|
|
}
|
|
|
|
void CSynapseClientSlot::ReleaseSO()
|
|
{
|
|
if (!mpDLL)
|
|
{
|
|
Syn_Printf("ERROR: no shared object handle for client '%s' in CSynapseClientSlot::ReleaseSO\n", mpClient->GetInfo());
|
|
return;
|
|
}
|
|
Syn_Printf("FreeLibrary '%s'\n", mpClient->GetInfo());
|
|
if (!FreeLibrary(mpDLL))
|
|
{
|
|
Syn_Printf(" FreeLibrary failed: GetLastError: '%s'\n", CSynapseServer::FormatGetLastError());
|
|
}
|
|
mpDLL = NULL;
|
|
}
|
|
|
|
#elif defined(__linux__) || defined (__APPLE__)
|
|
void CSynapseServer::EnumerateInterfaces(Str &soname)
|
|
{
|
|
CSynapseClientSlot slot;
|
|
slot.mpDLL = dlopen (soname.GetBuffer(), RTLD_NOW);
|
|
PFN_SYNAPSE_ENUMERATEINTERFACES *pEnumerate;
|
|
if (!slot.mpDLL)
|
|
{
|
|
char* error;
|
|
if ((error = (char *)dlerror()) == NULL)
|
|
error = "Unknown";
|
|
Syn_Printf("dlopen '%s' failed\n dlerror: '%s'\n", soname.GetBuffer(), error);
|
|
return;
|
|
}
|
|
slot.mpEnumerate = (PFN_SYNAPSE_ENUMERATEINTERFACES)dlsym(slot.mpDLL, NAME_SYNAPSE_ENUMERATEINTERFACES);
|
|
if (!slot.mpEnumerate)
|
|
{
|
|
char* error;
|
|
if ((error = (char *)dlerror()) == NULL)
|
|
error = "Unknown";
|
|
Syn_Printf("dlsym '%s' failed on shared object '%s'\n dlerror: '%s'\n", NAME_SYNAPSE_ENUMERATEINTERFACES, soname.GetBuffer(), error);
|
|
return;
|
|
}
|
|
Syn_Printf("Enumerate interfaces on '%s'\n", soname.GetBuffer());
|
|
slot.mpClient = slot.mpEnumerate(SYNAPSE_VERSION, this);
|
|
if (!slot.mpClient)
|
|
{
|
|
Syn_Printf("Enumerate interfaces on '%s' returned NULL, unloading.\n", soname.GetBuffer());
|
|
if (dlclose(slot.mpDLL))
|
|
{
|
|
char* error;
|
|
if ((error = (char *)dlerror()) == NULL)
|
|
error = "Unknown";
|
|
Syn_Printf(" dlclose failed: dlerror: '%s'\n", error);
|
|
}
|
|
return;
|
|
}
|
|
slot.mFileName = soname;
|
|
mClients.push_front(slot);
|
|
}
|
|
|
|
void CSynapseClientSlot::ReleaseSO()
|
|
{
|
|
if (!mpDLL)
|
|
{
|
|
Syn_Printf("ERROR: no shared object handle for client '%s' in CSynapseClientSlot::ReleaseSO\n", mpClient->GetInfo());
|
|
return;
|
|
}
|
|
Syn_Printf("dlclose '%s'\n", mpClient->GetInfo());
|
|
if (dlclose(mpDLL))
|
|
{
|
|
char* error;
|
|
if ((error = (char *)dlerror()) == NULL)
|
|
error = "Unknown";
|
|
Syn_Printf(" dlclose failed: dlerror: '%s'\n", error);
|
|
}
|
|
mpDLL = NULL;
|
|
}
|
|
|
|
#endif
|
|
|
|
void CSynapseServer::EnumerateBuiltinModule(CSynapseBuiltinClient *pClient)
|
|
{
|
|
CSynapseClientSlot slot;
|
|
pClient->EnumerateInterfaces(this);
|
|
slot.mpClient = pClient;
|
|
slot.mType = SYN_BUILTIN;
|
|
mClients.push_front(slot);
|
|
}
|
|
|
|
PFN_SYN_PRINTF_VA CSynapseServer::Get_Syn_Printf()
|
|
{
|
|
return g_pPrintf;
|
|
}
|
|
|
|
void CSynapseServer::TryPushStack(APIDescriptor_t *pAPI)
|
|
{
|
|
list<APIDescriptor_t*>::iterator iAPI;
|
|
for(iAPI=mStack.begin(); iAPI!=mStack.end(); iAPI++)
|
|
{
|
|
if ((*iAPI) == pAPI)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
mStack.push_front(pAPI);
|
|
mbStackChanged = true;
|
|
}
|
|
|
|
list<CSynapseClientSlot>::iterator CSynapseServer::ShutdownClient(list<CSynapseClientSlot>::iterator iSlot)
|
|
{
|
|
CSynapseClientSlot *pClientSlot = &(*iSlot);
|
|
if (pClientSlot->mpClient->IsActive())
|
|
{
|
|
// this should not happen except during core shutdown (i.e. editor is shutting down)
|
|
Syn_Printf("WARNING: ShutdownClient attempted on an active module '%s'\n", pClientSlot->mpClient->GetInfo());
|
|
}
|
|
// cleanup mStack
|
|
int i,api_count;
|
|
api_count = pClientSlot->mpClient->GetAPICount();
|
|
for(i=0; i<api_count; i++)
|
|
{
|
|
APIDescriptor_t *pAPI = pClientSlot->mpClient->GetAPIDescriptor(i);
|
|
// search this API in mStack
|
|
list< APIDescriptor_t *>::iterator iStack = mStack.begin();
|
|
while(iStack != mStack.end())
|
|
{
|
|
if (*iStack == pAPI)
|
|
break;
|
|
iStack++;
|
|
}
|
|
if (iStack != mStack.end())
|
|
{
|
|
if (pAPI->mType == SYN_REQUIRE)
|
|
{
|
|
if (pAPI->mbTableInitDone)
|
|
{
|
|
// even if non active, some SYN_REQUIRE may have been filled up
|
|
// look for the corresponding SYN_PROVIDE and decref
|
|
list< APIDescriptor_t *>::iterator iStackRequire = mStack.begin();
|
|
APIDescriptor_t *pMatchAPI;
|
|
while (iStackRequire != mStack.end())
|
|
{
|
|
pMatchAPI = *iStackRequire;
|
|
if ( pMatchAPI->mType == SYN_PROVIDE && MatchAPI( pMatchAPI, pAPI ) )
|
|
break;
|
|
iStackRequire++;
|
|
}
|
|
if (iStackRequire != mStack.end())
|
|
{
|
|
// we have found the corresponding SYN_PROVIDE
|
|
pMatchAPI->mRefCount--;
|
|
}
|
|
else
|
|
{
|
|
// this is not supposed to happen at all
|
|
Syn_Printf("ERROR: couldn't find the SYN_PROVIDE for an initialized SYN_REQUIRE API '%s' '%s' '%s'\n", pAPI->major_name, pAPI->minor_name, pClientSlot->mpClient->GetInfo());
|
|
}
|
|
}
|
|
}
|
|
else if (pAPI->mType == SYN_PROVIDE)
|
|
{
|
|
// this should never happen on non active clients, it may happen during a core shutdown though
|
|
// if the mRefCount is != 0, that means there is at least a function table out there that will segfault things
|
|
Syn_Printf("WARNING: found a SYN_PROVIDE API '%s' '%s' with refcount %d in CSynapseServer::ShutdownClient for '%s'\n", pAPI->major_name, pAPI->minor_name, pAPI->mRefCount, pClientSlot->mpClient->GetInfo());
|
|
}
|
|
// mostly safe to remove it now
|
|
mStack.erase(iStack);
|
|
}
|
|
}
|
|
// we can actually release the module now
|
|
// NOTE: do we want to have a 'final shutdown' call to the client? (not as long as we don't have a use for it)
|
|
if (pClientSlot->mType == SYN_SO)
|
|
{
|
|
pClientSlot->ReleaseSO();
|
|
}
|
|
return mClients.erase(iSlot);
|
|
}
|
|
|
|
void CSynapseServer::PushRequired(CSynapseClient *pClient)
|
|
{
|
|
/* walk through the standard APIs and push them in */
|
|
int i,max = pClient->GetAPICount();
|
|
for(i=0; i<max; i++)
|
|
{
|
|
APIDescriptor_t* pAPI = pClient->GetAPIDescriptor(i);
|
|
if (pAPI->mType == SYN_REQUIRE && !pAPI->mbTableInitDone)
|
|
{
|
|
TryPushStack(pAPI);
|
|
}
|
|
}
|
|
|
|
/* if this client has 'List' API Manager types, walk through them for addition too */
|
|
max = pClient->GetManagerListCount();
|
|
for(i=0; i<max; i++)
|
|
{
|
|
CSynapseAPIManager *pManager = pClient->GetManagerList(i);
|
|
assert(pManager->GetType() == API_LIST);
|
|
pManager->InitializeAPIList();
|
|
int j;
|
|
for(j=0; j<pManager->GetAPICount(); j++)
|
|
{
|
|
TryPushStack(pManager->GetAPI(j));
|
|
}
|
|
}
|
|
|
|
/* if there are loose match managers, prompt them against the current list of SYN_PROVIDE interfaces
|
|
* and let them decide which ones they might want
|
|
*/
|
|
|
|
max = pClient->GetManagerMatchCount();
|
|
|
|
for(i=0; i<max; i++)
|
|
{
|
|
CSynapseAPIManager *pManager = pClient->GetManagerMatch(i);
|
|
// start matching all known SYN_PROVIDE APIs against this manager
|
|
list<CSynapseClientSlot>::iterator iClientSlot;
|
|
for(iClientSlot=mClients.begin(); iClientSlot!=mClients.end(); iClientSlot++)
|
|
{
|
|
CSynapseClient *pScanClient = (*iClientSlot).
|
|
mpClient;
|
|
int j,jmax = pScanClient->GetAPICount();
|
|
for(j=0; j<jmax; j++)
|
|
{
|
|
APIDescriptor_t *pAPI = pScanClient->GetAPIDescriptor(j);
|
|
if (pAPI->mType == SYN_PROVIDE)
|
|
{
|
|
if (pManager->MatchAPI(pAPI->major_name, pAPI->minor_name))
|
|
{
|
|
/*! we are going to want to load this one
|
|
* NOTE TTimo: what if this can not be resolved in the end?
|
|
* if this happens, then the whole startup will fail instead
|
|
* or we can use SYN_REQUIRE_ANY and drop it without consequences
|
|
*/
|
|
APIDescriptor_t *pPushAPI = pManager->BuildRequireAPI(pAPI);
|
|
TryPushStack(pPushAPI);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CSynapseServer::MatchAPI(APIDescriptor_t *p1, APIDescriptor_t *p2)
|
|
{
|
|
return MatchAPI( p1->major_name, p1->minor_name, p2->major_name, p2->minor_name );
|
|
}
|
|
|
|
bool CSynapseServer::MatchAPI(const char* major1, const char* minor1, const char* major2, const char* minor2)
|
|
{
|
|
if ( strcmp( major1, major2 ) ) {
|
|
return false;
|
|
}
|
|
// either no minor at all for this API, or matching
|
|
if ( ( minor1 && minor2 ) && !strcmp( minor1, minor2 ) ) {
|
|
return true;
|
|
}
|
|
// or one of the minors says "*" (knowing that the majors match already)
|
|
if ( ( minor1 && !strcmp( minor1, "*" ) ) || ( minor2 && !strcmp( minor2, "*" ) ) ) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CSynapseServer::ResolveAPI(APIDescriptor_t* pAPI)
|
|
{
|
|
//Syn_Printf("In ResolveAPI %s %p '%s' '%s'\n", APITypeName[pAPI->mType], pAPI, pAPI->major_name, pAPI->minor_name);
|
|
// loop through active clients, search for a client providing what we are looking for
|
|
list<CSynapseClientSlot>::iterator iClient;
|
|
for(iClient=mClients.begin(); iClient!=mClients.end(); iClient++)
|
|
{
|
|
// walk through interfaces on this client for a match
|
|
CSynapseClient *pScanClient = (*iClient).mpClient;
|
|
int i,max = pScanClient->GetAPICount();
|
|
for(i=0; i<max; i++)
|
|
{
|
|
APIDescriptor_t *pScanClientAPI = pScanClient->GetAPIDescriptor(i);
|
|
if (pScanClientAPI->mType == SYN_PROVIDE)
|
|
{
|
|
if (MatchAPI(pAPI, pScanClientAPI))
|
|
{
|
|
// can this client provide APIs yet
|
|
// it is possible that all of it's APIs have been filled and it's not been activated yet
|
|
pScanClient->CheckSetActive();
|
|
if (pScanClient->IsActive())
|
|
{
|
|
// make sure this interface has correct size (this is our version check)
|
|
if (pAPI->mSize != pScanClientAPI->mSize)
|
|
{
|
|
Syn_Printf("ERROR: version mismatch for API '%s' '%s' found in '%s' (size %d != %d)\n", pAPI->major_name, pAPI->minor_name, pScanClient->GetInfo(), pAPI->mSize, pScanClientAPI->mSize);
|
|
Syn_Printf(" the module and the server are incompatible\n");
|
|
// keep going to other APIs
|
|
continue;
|
|
}
|
|
// this is an active client, we can request
|
|
#ifdef SYNAPSE_VERBOSE
|
|
Syn_Printf("RequestAPI '%s' '%s' from '%s' for API %p\n", pAPI->major_name, pAPI->minor_name, pScanClient->GetInfo(), pAPI);
|
|
#endif
|
|
if (!pScanClient->RequestAPI(pAPI))
|
|
{
|
|
// this should never happen, means we think this module provides the API, but it answers that it doesn't
|
|
Syn_Printf("ERROR: RequestAPI failed\n");
|
|
return false;
|
|
}
|
|
pScanClientAPI->mRefCount++;
|
|
pAPI->mbTableInitDone = true;
|
|
return true; // job done
|
|
}
|
|
else
|
|
{
|
|
// this client is not active yet, some of it's required interfaces are not filled in
|
|
PushRequired(pScanClient);
|
|
// we will exit the scan through the APIDescriptor of this client and look at other clients
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CSynapseServer::DoResolve(CSynapseClient *pClient)
|
|
{
|
|
list<CSynapseClientSlot>::iterator iSlot;
|
|
for(iSlot=mClients.begin(); iSlot != mClients.end(); iSlot++)
|
|
{
|
|
if ((*iSlot).mpClient == pClient)
|
|
break;
|
|
}
|
|
if (iSlot == mClients.end())
|
|
{
|
|
Syn_Printf("CSynapserServer::Resolve adding new client slot '%s'\n", pClient->GetInfo());
|
|
CSynapseClientSlot slot;
|
|
slot.mpClient = pClient;
|
|
slot.mFileName = "local client";
|
|
// make it active so we can request the interfaces already
|
|
pClient->ForceSetActive();
|
|
mClients.push_front(slot);
|
|
}
|
|
else
|
|
{
|
|
// make it active so we can request the interfaces already
|
|
(*iSlot).mpClient->ForceSetActive();
|
|
}
|
|
|
|
// push the interfaces that need to be resolved for this client
|
|
// NOTE: this doesn't take care of the SYN_REQUIRE_ANY interfaces
|
|
PushRequired(pClient);
|
|
// start resolving now
|
|
// working till the stack is emptied or till we reach a dead end situation
|
|
// we do a depth first traversal, we will grow the interface stack to be resolved till we start finding solutions
|
|
list<APIDescriptor_t*>::iterator iCurrent;
|
|
mbStackChanged = true; // init to true so we try the first elem
|
|
while (!mStack.empty())
|
|
{
|
|
//DumpStack();
|
|
if (!mbStackChanged)
|
|
{
|
|
// the stack didn't change last loop
|
|
iCurrent++;
|
|
if (iCurrent==mStack.end())
|
|
{
|
|
Syn_Printf("ERROR: CSynapseServer::Resolve, failed to resolve\n");
|
|
DumpStack();
|
|
return false;
|
|
}
|
|
if (ResolveAPI(*iCurrent))
|
|
{
|
|
iCurrent = mStack.erase(iCurrent);
|
|
mbStackChanged = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// the stack changed at last loop
|
|
mbStackChanged = false;
|
|
iCurrent = mStack.begin();
|
|
if (ResolveAPI(*iCurrent))
|
|
{
|
|
iCurrent = mStack.erase(iCurrent);
|
|
mbStackChanged = true;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CSynapseServer::Resolve(CSynapseClient *pClient)
|
|
{
|
|
bool ret = DoResolve(pClient);
|
|
list<CSynapseClientSlot>::iterator iClient;
|
|
iClient = mClients.begin();
|
|
while(iClient != mClients.end())
|
|
{
|
|
CSynapseClient *pClient = (*iClient).mpClient;
|
|
if (!pClient->IsActive())
|
|
{
|
|
Syn_Printf("Unloading an unused module: '%s'\n", pClient->GetInfo());
|
|
iClient = ShutdownClient(iClient);
|
|
}
|
|
else
|
|
iClient++;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void CSynapseServer::Shutdown()
|
|
{
|
|
Syn_Printf("Synapse server core is shutting down\n");
|
|
// do a first pass to shutdown the clients nicely (i.e. decref, release memory and drop everything)
|
|
// we seperate the client shutdown calls from the dlclose cause that part is a clean decref / free situation whereas dlclose will break links without advice
|
|
list<CSynapseClientSlot>::iterator iClient;
|
|
iClient = mClients.begin();
|
|
for(iClient = mClients.begin(); iClient != mClients.end(); iClient++)
|
|
{
|
|
(*iClient).mpClient->Shutdown();
|
|
}
|
|
// now release them from the server's point of view
|
|
iClient = mClients.begin();
|
|
while(iClient != mClients.end())
|
|
{
|
|
iClient = ShutdownClient(iClient);
|
|
}
|
|
}
|
|
|
|
void CSynapseServer::DumpStack()
|
|
{
|
|
list<APIDescriptor_t*>::iterator iCurrent;
|
|
for(iCurrent=mStack.begin(); iCurrent != mStack.end(); iCurrent++)
|
|
{
|
|
APIDescriptor_t*pAPI = *iCurrent;
|
|
Syn_Printf("interface %s %p '%s' '%s'\n", APITypeName[pAPI->mType], pAPI, pAPI->major_name, pAPI->minor_name);
|
|
}
|
|
}
|
|
|
|
void CSynapseServer::DumpActiveClients()
|
|
{
|
|
list<CSynapseClientSlot>::iterator iClient;
|
|
for(iClient=mClients.begin(); iClient!=mClients.end(); iClient++)
|
|
{
|
|
CSynapseClient *pClient = (*iClient).mpClient;
|
|
Syn_Printf("%s", pClient->GetInfo());
|
|
if (pClient->IsActive())
|
|
Syn_Printf("\n");
|
|
else
|
|
Syn_Printf(" (not active)\n");
|
|
}
|
|
}
|
|
|
|
bool CSynapseServer::SelectClientConfig(const char *client_name)
|
|
{
|
|
if (!mpDoc)
|
|
return false;
|
|
xmlNodePtr pNode = xmlDocGetRootElement(mpDoc);
|
|
if (!pNode)
|
|
return false;
|
|
// look for the client
|
|
pNode=pNode->children;
|
|
while(pNode)
|
|
{
|
|
if (pNode->type == XML_ELEMENT_NODE)
|
|
{
|
|
xmlChar *prop = xmlGetProp(pNode, (const xmlChar *)"name");
|
|
if (!strcmp((const char *)prop, client_name))
|
|
{
|
|
xmlFree(prop);
|
|
break;
|
|
}
|
|
xmlFree(prop);
|
|
}
|
|
pNode = pNode->next;
|
|
}
|
|
if (!pNode)
|
|
return false; // config you asked for isn't there
|
|
// focus
|
|
mpFocusedNode = pNode->children;
|
|
mpCurrentClientConfig = pNode;
|
|
return true;
|
|
}
|
|
|
|
bool CSynapseServer::GetNextConfig(char **api_name, char **minor)
|
|
{
|
|
while(mpFocusedNode && mpFocusedNode->name)
|
|
{
|
|
if (mpFocusedNode->type == XML_ELEMENT_NODE && !strcmp((const char *)mpFocusedNode->name, "api"))
|
|
{
|
|
if (m_api_name)
|
|
xmlFree(m_api_name);
|
|
m_api_name = xmlGetProp(mpFocusedNode, (const xmlChar *)"name");
|
|
*api_name = (char *)m_api_name;
|
|
if (m_content)
|
|
g_free(m_content);
|
|
m_content = g_strdup((const gchar *)mpFocusedNode->children->content);
|
|
g_strstrip(m_content);
|
|
*minor = m_content;
|
|
mpFocusedNode = mpFocusedNode->next;
|
|
return true;
|
|
}
|
|
mpFocusedNode = mpFocusedNode->next;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CSynapseServer::GetConfigForAPI( const char *api, char **minor ) {
|
|
xmlNodePtr pNode = mpCurrentClientConfig->children;
|
|
while ( pNode && pNode->name ) {
|
|
if ( pNode->type == XML_ELEMENT_NODE && !strcmp( (const char *)pNode->name, "api" ) ) {
|
|
if ( m_api_name ) {
|
|
xmlFree( m_api_name );
|
|
}
|
|
m_api_name = xmlGetProp( pNode, (const xmlChar *)"name" );
|
|
if ( !strcmp( (const char *)m_api_name, api ) ) {
|
|
if ( m_content ) {
|
|
g_free( m_content );
|
|
}
|
|
m_content = g_strdup( (const gchar *)pNode->children->content );
|
|
g_strstrip( m_content );
|
|
*minor = m_content;
|
|
return true;
|
|
}
|
|
}
|
|
pNode = pNode->next;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const char *CSynapseServer::GetModuleFilename(CSynapseClient *pClient)
|
|
{
|
|
list<CSynapseClientSlot>::iterator iSlot;
|
|
for(iSlot=mClients.begin(); iSlot != mClients.end(); iSlot++)
|
|
{
|
|
if ((*iSlot).mpClient == pClient)
|
|
{
|
|
if ((*iSlot).mType == SYN_BUILTIN)
|
|
{
|
|
return ""; // FIXME
|
|
}
|
|
else
|
|
{
|
|
return (*iSlot).mFileName;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
=======================================================================
|
|
client
|
|
=======================================================================
|
|
*/
|
|
|
|
CSynapseClient::CSynapseClient()
|
|
{
|
|
}
|
|
|
|
void CSynapseClient::Shutdown()
|
|
{
|
|
vector<APIDescriptor_t *>::iterator iAPI;
|
|
for(iAPI=mAPIDescriptors.begin(); iAPI!=mAPIDescriptors.end(); iAPI++)
|
|
{
|
|
APIDescriptor_t *pAPI = *iAPI;
|
|
if (pAPI->mRefCount != 0)
|
|
Syn_Printf("WARNING: ~CSynapseClient '%s' has non-zero ref count for interface '%s' '%s'\n", GetInfo(), pAPI->major_name, pAPI->minor_name);
|
|
else
|
|
delete pAPI;
|
|
*iAPI=NULL;
|
|
}
|
|
mAPIDescriptors.clear();
|
|
vector<CSynapseAPIManager *>::iterator iManager;
|
|
for(iManager=mManagersList.begin(); iManager!=mManagersList.end(); iManager++)
|
|
{
|
|
CSynapseAPIManager *pManager = *iManager;
|
|
pManager->DecRef();
|
|
*iManager = NULL;
|
|
}
|
|
mManagersList.clear();
|
|
for(iManager=mManagersMatch.begin(); iManager!=mManagersMatch.end(); iManager++)
|
|
{
|
|
CSynapseAPIManager *pManager = *iManager;
|
|
pManager->DecRef();
|
|
*iManager = NULL;
|
|
}
|
|
mManagersMatch.clear();
|
|
}
|
|
|
|
CSynapseClient::~CSynapseClient()
|
|
{
|
|
// this should not be doing anything when called from here if everything went right
|
|
// otherwise it's likely to crash .. at least that's the sign we missed something
|
|
Shutdown();
|
|
}
|
|
|
|
int CSynapseClient::GetAPICount()
|
|
{
|
|
return mAPIDescriptors.size();
|
|
}
|
|
|
|
APIDescriptor_t* CSynapseClient::GetAPIDescriptor(int i)
|
|
{
|
|
return mAPIDescriptors[i];
|
|
}
|
|
|
|
int CSynapseClient::GetManagerMatchCount()
|
|
{
|
|
return mManagersMatch.size();
|
|
}
|
|
|
|
CSynapseAPIManager* CSynapseClient::GetManagerMatch(int i)
|
|
{
|
|
return mManagersMatch[i];
|
|
}
|
|
|
|
int CSynapseClient::GetManagerListCount()
|
|
{
|
|
return mManagersList.size();
|
|
}
|
|
|
|
CSynapseAPIManager* CSynapseClient::GetManagerList(int i)
|
|
{
|
|
return mManagersList[i];
|
|
}
|
|
|
|
bool CSynapseClient::AddAPI(const char *major, const char *minor, int size, EAPIType type, void *pTable)
|
|
{
|
|
// do some safe checks before actual addition
|
|
if (type == SYN_REQUIRE && !pTable)
|
|
{
|
|
Syn_Printf("ERROR: interface '%s' '%s' from '%s' is SYN_REQUIRE and doesn't provide a function table pointer\n", major, minor, GetInfo());
|
|
return false;
|
|
}
|
|
if (pTable)
|
|
{
|
|
int *pi = (int *)pTable;
|
|
if (pi == 0)
|
|
{
|
|
Syn_Printf("ERROR: forgot to init function table size for interface '%s' '%s' from '%s'?\n", major, minor, GetInfo());
|
|
return false;
|
|
}
|
|
}
|
|
APIDescriptor_t *pAPI = new APIDescriptor_t;
|
|
memset(pAPI, 0, sizeof(APIDescriptor_t));
|
|
strncpy(pAPI->major_name, major, MAX_APINAME);
|
|
if (minor)
|
|
strncpy(pAPI->minor_name, minor, MAX_APINAME);
|
|
pAPI->mType = type;
|
|
pAPI->mpTable = pTable;
|
|
// store the interface size
|
|
if (type == SYN_PROVIDE)
|
|
{
|
|
if (size == 0)
|
|
{
|
|
Syn_Printf("ERROR: size of the interface required for a SYN_PROVIDE ('%s' '%s' from '%s')\n", major, minor, GetInfo());
|
|
delete pAPI;
|
|
return false;
|
|
}
|
|
pAPI->mSize = size;
|
|
}
|
|
else if (type == SYN_REQUIRE)
|
|
{
|
|
if (size != 0)
|
|
{
|
|
// if a non-zero value is given in function call, use this instead of the val in table
|
|
*((int *)pAPI->mpTable) = size;
|
|
pAPI->mSize = size;
|
|
}
|
|
else
|
|
{
|
|
pAPI->mSize = *((int *)pAPI->mpTable);
|
|
if (pAPI->mSize == 0)
|
|
{
|
|
Syn_Printf("ERROR: didn't get an interface size ('%s' '%s' from '%s')\n", major, minor, GetInfo());
|
|
delete pAPI;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Syn_Printf("ERROR: AddAPI type '%d' not supported\n", type);
|
|
return false;
|
|
}
|
|
mAPIDescriptors.push_back(pAPI);
|
|
#ifdef SYNAPSE_VERBOSE
|
|
Syn_Printf("AddAPI: %s %p '%s' '%s' from '%s', size %d\n", APITypeName[pAPI->mType], pAPI, major, minor, GetInfo(), pAPI->mSize);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
#include "version.h"
|
|
|
|
const char* CSynapseClient::GetInfo()
|
|
{
|
|
return "CSynapseClient built " __DATE__ " " RADIANT_VERSION;
|
|
}
|
|
|
|
bool CSynapseClient::CheckSetActive()
|
|
{
|
|
if (mbActive)
|
|
return true;
|
|
int i,max=GetAPICount();
|
|
for(i=0; i<max; i++)
|
|
{
|
|
APIDescriptor_t *pAPI = GetAPIDescriptor(i);
|
|
if (pAPI->mType == SYN_REQUIRE && !pAPI->mbTableInitDone)
|
|
return false;
|
|
}
|
|
// if we have managers with fixed list, those need to be completely filled in too
|
|
vector<CSynapseAPIManager *>::iterator iManager;
|
|
for(iManager=mManagersList.begin(); iManager!=mManagersList.end(); iManager++)
|
|
{
|
|
if (!(*iManager)->CheckSetActive())
|
|
return false; // one of the managers doesn't have all it needs yet
|
|
}
|
|
// call OnActivate to let the client perform last minute checks
|
|
// NOTE: this should be fatal instead of letting the engine try other combinations
|
|
if (!OnActivate()) {
|
|
return false;
|
|
}
|
|
// yes, all required interfaces have been initialized
|
|
Syn_Printf("'%s' activated\n", GetInfo());
|
|
mbActive = true;
|
|
return true;
|
|
}
|
|
|
|
bool CSynapseClient::ConfigXML( CSynapseServer *pServer, const char *client_name, const XMLConfigEntry_t entries[] ) {
|
|
|
|
if ( !client_name ) {
|
|
client_name = GetName();
|
|
}
|
|
|
|
Syn_Printf("Dynamic APIs for client '%s'\n", GetInfo());
|
|
if ( !pServer->SelectClientConfig( client_name ) )
|
|
{
|
|
Syn_Printf( "Failed to select synapse client config '%s'\n", client_name );
|
|
return false;
|
|
}
|
|
|
|
int i = 0;
|
|
while ( entries[i].type != SYN_UNKNOWN ) { // don't test pTable, for a SYN_PROVIDE it will be empty
|
|
char *minor;
|
|
if ( !pServer->GetConfigForAPI( entries[i].api, &minor ) ) {
|
|
Syn_Printf( "GetConfigForAPI '%s' failed - invalid XML config file?\n", entries[i].api );
|
|
return false;
|
|
}
|
|
AddAPI( entries[i].api, minor, entries[i].size, entries[i].type, entries[i].pTable );
|
|
i++;
|
|
}
|
|
Syn_Printf( "%d dynamic interfaces parsed for '%s'\n", i, client_name );
|
|
return true;
|
|
}
|
|
|
|
void CSynapseClient::AddManager(CSynapseAPIManager *pManager)
|
|
{
|
|
pManager->IncRef();
|
|
if (pManager->GetType() == API_LIST)
|
|
mManagersList.push_back(pManager);
|
|
else
|
|
mManagersMatch.push_back(pManager);
|
|
}
|
|
|
|
CSynapseAPIManager::~CSynapseAPIManager()
|
|
{
|
|
vector<APIDescriptor_t *>::iterator iAPI;
|
|
for(iAPI=mAPIs.begin(); iAPI!=mAPIs.end(); iAPI++)
|
|
{
|
|
APIDescriptor_t *pAPI = *iAPI;
|
|
if (pAPI->mRefCount != 0)
|
|
{
|
|
Syn_Printf("WARNING: ~CSynapseAPIManager has non-zero ref count for interface '%s' '%s'\n", pAPI->major_name, pAPI->minor_name);
|
|
}
|
|
delete pAPI;
|
|
*iAPI = NULL;
|
|
}
|
|
}
|
|
|
|
APIDescriptor_t* CSynapseAPIManager::PrepareRequireAPI(APIDescriptor_t *pAPI)
|
|
{
|
|
#ifdef _DEBUG
|
|
if (pAPI->mType != SYN_PROVIDE)
|
|
{
|
|
Syn_Printf("ERROR: unexpected pAPI->mType != SYN_PROVIDE in CSynapseAPIManager::PrepareRequireAPI\n");
|
|
return NULL;
|
|
}
|
|
#endif
|
|
APIDescriptor_t *pRequireAPI = new APIDescriptor_t;
|
|
memcpy(pRequireAPI, pAPI, sizeof(APIDescriptor_t));
|
|
pRequireAPI->mType = SYN_REQUIRE_ANY;
|
|
pRequireAPI->mpTable = NULL;
|
|
pRequireAPI->mbTableInitDone = false;
|
|
pRequireAPI->mSize = 0; // this will have to be set correctly by the child for version checking
|
|
pRequireAPI->mRefCount = 0;
|
|
return pRequireAPI;
|
|
}
|
|
|
|
void CSynapseAPIManager::SetMatchAPI(const char *major, const char *minor)
|
|
{
|
|
if (strlen(minor)>MAX_PATTERN_STRING)
|
|
{
|
|
Syn_Printf("ERROR: MAX_TOKEN_STRING exceeded in CSynapseAPIManager::SetMatchAPI: '%s'\n", minor);
|
|
return;
|
|
}
|
|
strcpy(major_pattern, major);
|
|
strcpy(minor_pattern, minor);
|
|
if (strcmp(minor, "*"))
|
|
{
|
|
mType = API_LIST;
|
|
}
|
|
}
|
|
|
|
bool CSynapseAPIManager::MatchAPI(const char *major, const char *minor)
|
|
{
|
|
assert(mType == API_MATCH);
|
|
|
|
/*!
|
|
if this interface has been allocated already, avoid requesting it again..
|
|
*/
|
|
vector<APIDescriptor_t *>::iterator iAPI;
|
|
for(iAPI=mAPIs.begin(); iAPI!=mAPIs.end(); iAPI++)
|
|
{
|
|
if (CSynapseServer::MatchAPI((*iAPI)->major_name, (*iAPI)->minor_name, major, minor))
|
|
return false;
|
|
}
|
|
|
|
if (!strcmp(major, major_pattern))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool CSynapseAPIManager::CheckSetActive()
|
|
{
|
|
if (mType == API_MATCH)
|
|
return false;
|
|
// mType == API_LIST
|
|
int i,max = GetAPICount();
|
|
for(i=0; i<max; i++)
|
|
{
|
|
if (!GetAPI(i)->mbTableInitDone)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CSynapseAPIManager::InitializeAPIList()
|
|
{
|
|
char minor_tok[MAX_PATTERN_STRING];
|
|
char *token;
|
|
|
|
if (mAPIs.size())
|
|
{
|
|
Syn_Printf("WARNING: CSynapseAPIManager::InitializeAPIList on an already initialized APIManager\n");
|
|
return;
|
|
}
|
|
|
|
strncpy(minor_tok, minor_pattern, MAX_PATTERN_STRING);
|
|
token = strtok(minor_tok, " ");
|
|
while (token)
|
|
{
|
|
/* ask the child to build from scratch */
|
|
APIDescriptor_t *pAPI = new APIDescriptor_t;
|
|
memset(pAPI, 0, sizeof(APIDescriptor_t));
|
|
strncpy(pAPI->major_name, major_pattern, MAX_APINAME);
|
|
strncpy(pAPI->minor_name, token, MAX_APINAME);
|
|
pAPI->mType = SYN_REQUIRE_ANY;
|
|
FillAPITable(pAPI);
|
|
mAPIs.push_back(pAPI);
|
|
token = strtok(NULL, " ");
|
|
}
|
|
}
|
|
|
|
int CSynapseAPIManager::GetAPICount()
|
|
{
|
|
return mAPIs.size();
|
|
}
|
|
|
|
APIDescriptor_t* CSynapseAPIManager::GetAPI(int i)
|
|
{
|
|
return mAPIs[i];
|
|
}
|
|
|
|
// http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=879
|
|
void fini_stub() {
|
|
printf( "fini_stub\n" );
|
|
}
|