/* 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); if (!slot.mpDLL) { const 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) { const 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)) { const 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)) { const 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]; }