X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fnetradiant.git;a=blobdiff_plain;f=libs%2Fsynapse%2Fsynapse.cpp;h=432a02c614bad0ffcf3b9efbdd5d486ea4109d1a;hp=d223d03f54052909d714c17fd342fbf5f414fc34;hb=33efc9089296fc4e5f54d43581a0db81576ba848;hpb=ff588a439f6496396c41d6bb4028e61bdfed38c7 diff --git a/libs/synapse/synapse.cpp b/libs/synapse/synapse.cpp index d223d03f..432a02c6 100644 --- a/libs/synapse/synapse.cpp +++ b/libs/synapse/synapse.cpp @@ -1,1101 +1,1101 @@ -/* -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 - -// seems to be required for str.h -#include -#include - -#include "synapse.h" -#if defined (__linux__) || defined (__APPLE__) - #include -#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::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::iterator iAPI; - for(iAPI=mStack.begin(); iAPI!=mStack.end(); iAPI++) - { - if ((*iAPI) == pAPI) - { - return; - } - } - mStack.push_front(pAPI); - mbStackChanged = true; -} - -list::iterator CSynapseServer::ShutdownClient(list::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; impClient->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; iGetAPIDescriptor(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; iGetManagerList(i); - assert(pManager->GetType() == API_LIST); - pManager->InitializeAPIList(); - int j; - for(j=0; jGetAPICount(); 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; iGetManagerMatch(i); - // start matching all known SYN_PROVIDE APIs against this manager - list::iterator iClientSlot; - for(iClientSlot=mClients.begin(); iClientSlot!=mClients.end(); iClientSlot++) - { - CSynapseClient *pScanClient = (*iClientSlot). - mpClient; - int j,jmax = pScanClient->GetAPICount(); - for(j=0; jGetAPIDescriptor(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::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; iGetAPIDescriptor(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::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::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::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::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::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::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::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::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::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; imType == SYN_REQUIRE && !pAPI->mbTableInitDone) - return false; - } - // if we have managers with fixed list, those need to be completely filled in too - vector::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::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::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; imbTableInitDone) - 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" ); -} +/* +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 + +// seems to be required for str.h +#include +#include + +#include "synapse.h" +#if defined (__linux__) || defined (__APPLE__) + #include +#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::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::iterator iAPI; + for(iAPI=mStack.begin(); iAPI!=mStack.end(); iAPI++) + { + if ((*iAPI) == pAPI) + { + return; + } + } + mStack.push_front(pAPI); + mbStackChanged = true; +} + +list::iterator CSynapseServer::ShutdownClient(list::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; impClient->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; iGetAPIDescriptor(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; iGetManagerList(i); + assert(pManager->GetType() == API_LIST); + pManager->InitializeAPIList(); + int j; + for(j=0; jGetAPICount(); 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; iGetManagerMatch(i); + // start matching all known SYN_PROVIDE APIs against this manager + list::iterator iClientSlot; + for(iClientSlot=mClients.begin(); iClientSlot!=mClients.end(); iClientSlot++) + { + CSynapseClient *pScanClient = (*iClientSlot). + mpClient; + int j,jmax = pScanClient->GetAPICount(); + for(j=0; jGetAPIDescriptor(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::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; iGetAPIDescriptor(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::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::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::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::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::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::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::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::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::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; imType == SYN_REQUIRE && !pAPI->mbTableInitDone) + return false; + } + // if we have managers with fixed list, those need to be completely filled in too + vector::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::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::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; imbTableInitDone) + 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" ); +}