d223d03f54052909d714c17fd342fbf5f414fc34
[xonotic/netradiant.git] / libs / synapse / synapse.cpp
1 /*\r
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.\r
4 \r
5 This file is part of GtkRadiant.\r
6 \r
7 GtkRadiant is free software; you can redistribute it and/or modify\r
8 it under the terms of the GNU General Public License as published by\r
9 the Free Software Foundation; either version 2 of the License, or\r
10 (at your option) any later version.\r
11 \r
12 GtkRadiant is distributed in the hope that it will be useful,\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15 GNU General Public License for more details.\r
16 \r
17 You should have received a copy of the GNU General Public License\r
18 along with GtkRadiant; if not, write to the Free Software\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
20 */\r
21 \r
22 #include <assert.h>\r
23 \r
24 // seems to be required for str.h\r
25 #include <glib.h>\r
26 #include <glib/gstdio.h>\r
27 \r
28 #include "synapse.h"\r
29 #if defined (__linux__) || defined (__APPLE__)\r
30   #include <dirent.h>\r
31 #endif\r
32 \r
33 /*\r
34 ===================================================\r
35 diagnostic stuff\r
36 ===================================================\r
37 */\r
38 \r
39 extern "C"\r
40 {\r
41 \r
42 static PFN_SYN_PRINTF_VA g_pPrintf = NULL;  \r
43   \r
44 void Set_Syn_Printf(PFN_SYN_PRINTF_VA pf)\r
45 {\r
46   g_pPrintf = pf;\r
47 }\r
48 \r
49 #define BUFFER_SIZE 4096\r
50 \r
51 void Syn_Printf (const char *text, ...)\r
52 {\r
53   char buf[BUFFER_SIZE];\r
54   va_list args;\r
55   \r
56   if (!text)\r
57     return;\r
58   \r
59   if (g_pPrintf)\r
60   {\r
61     va_start (args, text);\r
62     (*g_pPrintf)(text, args);\r
63     va_end (args);\r
64   }\r
65   else\r
66   {\r
67     va_start (args, text);\r
68     vsnprintf (buf, BUFFER_SIZE, text, args);\r
69     buf[BUFFER_SIZE-1] = 0;\r
70     printf(buf);\r
71     va_end (args);\r
72   }\r
73 }\r
74 \r
75 }\r
76 \r
77 /*\r
78 =======================================================================\r
79 server\r
80 =======================================================================\r
81 */\r
82 \r
83 // this must be kept in sync with EAPIType\r
84 static const char* APITypeName[4] =\r
85 {\r
86   "SYN_UNKNOWN",\r
87   "SYN_PROVIDE",\r
88   "SYN_REQUIRE",\r
89   "SYN_REQUIRE_ANY"\r
90 };\r
91 \r
92 CSynapseServer::CSynapseServer()\r
93 {\r
94   mpDoc = NULL;\r
95   m_api_name = NULL;\r
96   m_content = NULL;\r
97   mpFocusedNode = NULL;\r
98 }\r
99 \r
100 CSynapseServer::~CSynapseServer()\r
101 {\r
102   if (m_api_name)\r
103     xmlFree(m_api_name);\r
104   if (m_content)\r
105     g_free(m_content);\r
106   Syn_Printf("TODO: free API managers\n");\r
107 }\r
108 \r
109 void CSynapseServer::AddSearchPath(char* path)\r
110 {\r
111   char *pLocalPath = new char[strlen(path)+1];\r
112   strcpy(pLocalPath, path);\r
113   mSearchPaths.push_front(pLocalPath);\r
114 }\r
115 \r
116 bool CSynapseServer::Initialize(const char* conf_file, PFN_SYN_PRINTF_VA pf)\r
117 {\r
118   // browse the paths to locate all potential modules\r
119   \r
120   Set_Syn_Printf(pf);\r
121   \r
122   if (conf_file)\r
123   {\r
124     // if a config file is specified and we fail to load it, we fail\r
125     Syn_Printf("loading synapse XML config file '%s'\n", conf_file);\r
126     mpDoc = xmlParseFile(conf_file);\r
127     if (!mpDoc)\r
128     {\r
129       Syn_Printf("'%s' invalid/not found\n", conf_file);\r
130       return false;\r
131     }    \r
132   }\r
133 \r
134   for (list<char *>::iterator iPath=mSearchPaths.begin(); iPath!=mSearchPaths.end(); iPath++)\r
135   {\r
136     const char* path = *iPath;\r
137 \r
138     Syn_Printf("Synapse Scanning modules path: %s\n", path);\r
139 \r
140     GDir* dir = g_dir_open (path, 0, NULL);\r
141 \r
142     if (dir != NULL)\r
143     {\r
144       while (1)\r
145       {\r
146         const gchar* name = g_dir_read_name(dir);\r
147         if(name == NULL)\r
148           break;\r
149 \r
150         // too small to be isolated in win32/ and linux/ directories..\r
151 #if defined(_WIN32)\r
152         const char* ext_so = ".dll";\r
153 #elif defined (__linux__) || defined (__APPLE__)\r
154         const char* ext_so = ".so";\r
155 #endif\r
156         const char* ext = strrchr (name, '.');\r
157         if ((ext == NULL) || (stricmp (ext, ext_so) != 0))\r
158           continue;\r
159 \r
160         Str newModule;\r
161         newModule.Format("%s%s", path, name);\r
162         Syn_Printf("Found '%s'\n", newModule.GetBuffer());      \r
163         EnumerateInterfaces(newModule);\r
164       }\r
165 \r
166       g_dir_close(dir);\r
167     }\r
168   }\r
169   return true;\r
170 }\r
171 \r
172 #if defined(_WIN32)  \r
173 #define FORMAT_BUFSIZE 2048\r
174 const char* CSynapseServer::FormatGetLastError()\r
175 {\r
176   static char buf[FORMAT_BUFSIZE];\r
177   FormatMessage(\r
178     FORMAT_MESSAGE_FROM_SYSTEM | \r
179     FORMAT_MESSAGE_IGNORE_INSERTS,\r
180     NULL,\r
181     GetLastError(),\r
182     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language\r
183     buf,\r
184     FORMAT_BUFSIZE,\r
185     NULL \r
186   );\r
187   return buf;\r
188 }\r
189 \r
190 void CSynapseServer::EnumerateInterfaces(Str &soname)\r
191 {\r
192   CSynapseClientSlot slot;\r
193   slot.mpDLL = LoadLibrary(soname.GetBuffer());\r
194   if (!slot.mpDLL)\r
195   {\r
196     Syn_Printf("LoadLibrary '%s' failed\n", soname.GetBuffer());\r
197     Syn_Printf("  GetLastError: %s", FormatGetLastError());\r
198     return;\r
199   }\r
200   slot.mpEnumerate = (PFN_SYNAPSE_ENUMERATEINTERFACES)GetProcAddress(slot.mpDLL, NAME_SYNAPSE_ENUMERATEINTERFACES);\r
201   if (!slot.mpEnumerate)\r
202   {\r
203     Syn_Printf("GetProcAddress('%s') failed\n", NAME_SYNAPSE_ENUMERATEINTERFACES);\r
204     Syn_Printf("  GetLastError: %s", FormatGetLastError());\r
205     return;\r
206   }\r
207   Syn_Printf("Enumerate interfaces on '%s'\n", soname.GetBuffer());\r
208   slot.mpClient = slot.mpEnumerate(SYNAPSE_VERSION, this);\r
209   if (!slot.mpClient)\r
210   {\r
211     Syn_Printf("Enumerate interfaces on '%s' returned NULL, unloading.\n", soname.GetBuffer());\r
212     if (!FreeLibrary(slot.mpDLL))\r
213     {\r
214       Syn_Printf(" FreeLibrary failed: GetLastError: '%s'\n", CSynapseServer::FormatGetLastError());\r
215     }\r
216     return;\r
217   }\r
218   slot.mFileName = soname;\r
219   mClients.push_front(slot);\r
220 }\r
221 \r
222 void CSynapseClientSlot::ReleaseSO()\r
223 {\r
224   if (!mpDLL)\r
225   {\r
226     Syn_Printf("ERROR: no shared object handle for client '%s' in CSynapseClientSlot::ReleaseSO\n", mpClient->GetInfo());\r
227     return;\r
228   }\r
229   Syn_Printf("FreeLibrary '%s'\n", mpClient->GetInfo());\r
230   if (!FreeLibrary(mpDLL))\r
231   {\r
232     Syn_Printf(" FreeLibrary failed: GetLastError: '%s'\n", CSynapseServer::FormatGetLastError());\r
233   }\r
234   mpDLL = NULL;\r
235 }\r
236 \r
237 #elif defined(__linux__) || defined (__APPLE__)\r
238 void CSynapseServer::EnumerateInterfaces(Str &soname)\r
239 {\r
240   CSynapseClientSlot slot;\r
241   slot.mpDLL = dlopen (soname.GetBuffer(), RTLD_NOW);\r
242   PFN_SYNAPSE_ENUMERATEINTERFACES *pEnumerate;\r
243   if (!slot.mpDLL)\r
244   {\r
245     char* error;\r
246     if ((error = (char *)dlerror()) == NULL)\r
247       error = "Unknown";\r
248     Syn_Printf("dlopen '%s' failed\n  dlerror: '%s'\n", soname.GetBuffer(), error);\r
249     return;\r
250   }\r
251   slot.mpEnumerate = (PFN_SYNAPSE_ENUMERATEINTERFACES)dlsym(slot.mpDLL, NAME_SYNAPSE_ENUMERATEINTERFACES);\r
252   if (!slot.mpEnumerate)\r
253   {\r
254     char* error;\r
255     if ((error = (char *)dlerror()) == NULL)\r
256       error = "Unknown";\r
257     Syn_Printf("dlsym '%s' failed on shared object '%s'\n  dlerror: '%s'\n", NAME_SYNAPSE_ENUMERATEINTERFACES, soname.GetBuffer(), error);\r
258     return;\r
259   }\r
260   Syn_Printf("Enumerate interfaces on '%s'\n", soname.GetBuffer());\r
261   slot.mpClient = slot.mpEnumerate(SYNAPSE_VERSION, this);\r
262   if (!slot.mpClient)\r
263   {\r
264     Syn_Printf("Enumerate interfaces on '%s' returned NULL, unloading.\n", soname.GetBuffer());\r
265     if (dlclose(slot.mpDLL))\r
266     {\r
267       char* error;\r
268       if ((error = (char *)dlerror()) == NULL)\r
269         error = "Unknown";\r
270       Syn_Printf("  dlclose failed: dlerror: '%s'\n", error);\r
271     }\r
272     return;\r
273   }\r
274   slot.mFileName = soname;\r
275   mClients.push_front(slot);\r
276 }\r
277 \r
278 void CSynapseClientSlot::ReleaseSO()\r
279 {\r
280   if (!mpDLL)\r
281   {\r
282     Syn_Printf("ERROR: no shared object handle for client '%s' in CSynapseClientSlot::ReleaseSO\n", mpClient->GetInfo());\r
283     return;\r
284   }\r
285   Syn_Printf("dlclose '%s'\n", mpClient->GetInfo());\r
286   if (dlclose(mpDLL))\r
287   {\r
288     char* error;\r
289     if ((error = (char *)dlerror()) == NULL)\r
290       error = "Unknown";\r
291     Syn_Printf("  dlclose failed: dlerror: '%s'\n", error);\r
292   }\r
293   mpDLL = NULL;\r
294 }\r
295 \r
296 #endif\r
297 \r
298 void CSynapseServer::EnumerateBuiltinModule(CSynapseBuiltinClient *pClient)\r
299 {\r
300   CSynapseClientSlot slot;\r
301   pClient->EnumerateInterfaces(this);\r
302   slot.mpClient = pClient;\r
303   slot.mType = SYN_BUILTIN;\r
304   mClients.push_front(slot);\r
305 }\r
306 \r
307 PFN_SYN_PRINTF_VA CSynapseServer::Get_Syn_Printf()\r
308 {\r
309   return g_pPrintf;\r
310 }\r
311 \r
312 void CSynapseServer::TryPushStack(APIDescriptor_t *pAPI)\r
313 {\r
314   list<APIDescriptor_t*>::iterator iAPI;\r
315   for(iAPI=mStack.begin(); iAPI!=mStack.end(); iAPI++)\r
316   {\r
317     if ((*iAPI) == pAPI)\r
318     {\r
319       return;\r
320     }\r
321   }\r
322   mStack.push_front(pAPI);      \r
323   mbStackChanged = true;  \r
324 }\r
325 \r
326 list<CSynapseClientSlot>::iterator CSynapseServer::ShutdownClient(list<CSynapseClientSlot>::iterator iSlot)\r
327 {\r
328   CSynapseClientSlot *pClientSlot = &(*iSlot);\r
329   if (pClientSlot->mpClient->IsActive())\r
330   {\r
331     // this should not happen except during core shutdown (i.e. editor is shutting down)\r
332     Syn_Printf("WARNING: ShutdownClient attempted on an active module '%s'\n", pClientSlot->mpClient->GetInfo());\r
333   }\r
334   // cleanup mStack\r
335   int i,api_count;\r
336   api_count = pClientSlot->mpClient->GetAPICount();\r
337   for(i=0; i<api_count; i++)\r
338   {\r
339     APIDescriptor_t *pAPI = pClientSlot->mpClient->GetAPIDescriptor(i);\r
340     // search this API in mStack\r
341     list< APIDescriptor_t *>::iterator iStack = mStack.begin();\r
342     while(iStack != mStack.end())\r
343     {\r
344       if (*iStack == pAPI)\r
345         break;\r
346       iStack++;\r
347     }\r
348     if (iStack != mStack.end())\r
349     {\r
350       if (pAPI->mType == SYN_REQUIRE)\r
351       {\r
352         if (pAPI->mbTableInitDone)\r
353         {\r
354           // even if non active, some SYN_REQUIRE may have been filled up\r
355           // look for the corresponding SYN_PROVIDE and decref\r
356           list< APIDescriptor_t *>::iterator iStackRequire = mStack.begin();\r
357           APIDescriptor_t *pMatchAPI;\r
358           while (iStackRequire != mStack.end())\r
359           {\r
360             pMatchAPI = *iStackRequire;\r
361             if ( pMatchAPI->mType == SYN_PROVIDE && MatchAPI( pMatchAPI, pAPI ) )\r
362               break;\r
363             iStackRequire++;\r
364           }\r
365           if (iStackRequire != mStack.end())\r
366           {\r
367             // we have found the corresponding SYN_PROVIDE\r
368             pMatchAPI->mRefCount--;\r
369           }\r
370           else\r
371           {\r
372             // this is not supposed to happen at all\r
373             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());\r
374           }\r
375         }\r
376       }      \r
377       else if (pAPI->mType == SYN_PROVIDE)\r
378       {\r
379         // this should never happen on non active clients, it may happen during a core shutdown though\r
380         // if the mRefCount is != 0, that means there is at least a function table out there that will segfault things\r
381         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());\r
382       }\r
383       // mostly safe to remove it now\r
384       mStack.erase(iStack);\r
385     }\r
386   }\r
387   // we can actually release the module now\r
388   // 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)\r
389   if (pClientSlot->mType == SYN_SO)\r
390   {\r
391     pClientSlot->ReleaseSO();\r
392   }  \r
393   return mClients.erase(iSlot);\r
394 }\r
395 \r
396 void CSynapseServer::PushRequired(CSynapseClient *pClient)\r
397 {\r
398   /* walk through the standard APIs and push them in */\r
399   int i,max = pClient->GetAPICount();  \r
400   for(i=0; i<max; i++)\r
401   {\r
402     APIDescriptor_t* pAPI = pClient->GetAPIDescriptor(i);\r
403     if (pAPI->mType == SYN_REQUIRE && !pAPI->mbTableInitDone)\r
404     {\r
405       TryPushStack(pAPI);\r
406     }\r
407   }\r
408   \r
409   /* if this client has 'List' API Manager types, walk through them for addition too */\r
410   max = pClient->GetManagerListCount();\r
411   for(i=0; i<max; i++)\r
412   {\r
413     CSynapseAPIManager *pManager = pClient->GetManagerList(i);\r
414     assert(pManager->GetType() == API_LIST);\r
415     pManager->InitializeAPIList();\r
416     int j;\r
417     for(j=0; j<pManager->GetAPICount(); j++)\r
418     {\r
419       TryPushStack(pManager->GetAPI(j));\r
420     }\r
421   }\r
422   \r
423   /* if there are loose match managers, prompt them against the current list of SYN_PROVIDE interfaces\r
424    * and let them decide which ones they might want\r
425    */\r
426   \r
427   max = pClient->GetManagerMatchCount();\r
428 \r
429   for(i=0; i<max; i++)\r
430   {\r
431     CSynapseAPIManager *pManager = pClient->GetManagerMatch(i);\r
432     // start matching all known SYN_PROVIDE APIs against this manager\r
433     list<CSynapseClientSlot>::iterator iClientSlot;\r
434     for(iClientSlot=mClients.begin(); iClientSlot!=mClients.end(); iClientSlot++)\r
435     {\r
436       CSynapseClient *pScanClient = (*iClientSlot).\r
437       mpClient;\r
438       int j,jmax = pScanClient->GetAPICount();\r
439       for(j=0; j<jmax; j++)\r
440       {\r
441         APIDescriptor_t *pAPI = pScanClient->GetAPIDescriptor(j);\r
442         if (pAPI->mType == SYN_PROVIDE)\r
443         {\r
444           if (pManager->MatchAPI(pAPI->major_name, pAPI->minor_name))\r
445           {\r
446             /*! we are going to want to load this one\r
447                * NOTE TTimo: what if this can not be resolved in the end?\r
448                * if this happens, then the whole startup will fail instead\r
449                * or we can use SYN_REQUIRE_ANY and drop it without consequences\r
450                */\r
451             APIDescriptor_t *pPushAPI = pManager->BuildRequireAPI(pAPI);\r
452             TryPushStack(pPushAPI);\r
453           }\r
454         }\r
455       }\r
456     }\r
457   }\r
458 }\r
459 \r
460 bool CSynapseServer::MatchAPI(APIDescriptor_t *p1, APIDescriptor_t *p2)\r
461 {\r
462   return MatchAPI( p1->major_name, p1->minor_name, p2->major_name, p2->minor_name );\r
463 }\r
464 \r
465 bool CSynapseServer::MatchAPI(const char* major1, const char* minor1, const char* major2, const char* minor2)\r
466 {\r
467   if ( strcmp( major1, major2 ) ) {\r
468     return false;\r
469   }\r
470   // either no minor at all for this API, or matching\r
471   if ( ( minor1 && minor2 ) && !strcmp( minor1, minor2 ) ) {\r
472     return true;\r
473   }\r
474   // or one of the minors says "*" (knowing that the majors match already)\r
475   if ( ( minor1 && !strcmp( minor1, "*" ) ) || ( minor2 && !strcmp( minor2, "*" ) ) ) {\r
476     return true;\r
477   }\r
478   return false;\r
479 }\r
480 \r
481 bool CSynapseServer::ResolveAPI(APIDescriptor_t* pAPI)\r
482 {\r
483   //Syn_Printf("In ResolveAPI %s %p '%s' '%s'\n", APITypeName[pAPI->mType], pAPI, pAPI->major_name, pAPI->minor_name);  \r
484   // loop through active clients, search for a client providing what we are looking for\r
485   list<CSynapseClientSlot>::iterator iClient;\r
486   for(iClient=mClients.begin(); iClient!=mClients.end(); iClient++)\r
487   {\r
488     // walk through interfaces on this client for a match\r
489     CSynapseClient *pScanClient = (*iClient).mpClient;\r
490     int i,max = pScanClient->GetAPICount();\r
491     for(i=0; i<max; i++)\r
492     {\r
493       APIDescriptor_t *pScanClientAPI = pScanClient->GetAPIDescriptor(i);\r
494       if (pScanClientAPI->mType == SYN_PROVIDE)\r
495       {\r
496         if (MatchAPI(pAPI, pScanClientAPI))\r
497         {\r
498           // can this client provide APIs yet\r
499           // it is possible that all of it's APIs have been filled and it's not been activated yet\r
500           pScanClient->CheckSetActive();\r
501           if (pScanClient->IsActive())\r
502           {\r
503             // make sure this interface has correct size (this is our version check)\r
504             if (pAPI->mSize != pScanClientAPI->mSize)\r
505             {\r
506               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);\r
507               Syn_Printf("  the module and the server are incompatible\n");\r
508               // keep going to other APIs\r
509               continue;\r
510             }\r
511             // this is an active client, we can request\r
512             #ifdef SYNAPSE_VERBOSE\r
513               Syn_Printf("RequestAPI '%s' '%s' from '%s' for API %p\n", pAPI->major_name, pAPI->minor_name, pScanClient->GetInfo(), pAPI);\r
514             #endif\r
515             if (!pScanClient->RequestAPI(pAPI))\r
516             {\r
517               // this should never happen, means we think this module provides the API, but it answers that it doesn't\r
518               Syn_Printf("ERROR: RequestAPI failed\n");\r
519               return false;\r
520             }\r
521             pScanClientAPI->mRefCount++;\r
522             pAPI->mbTableInitDone = true;\r
523             return true; // job done\r
524           }\r
525           else\r
526           {\r
527             // this client is not active yet, some of it's required interfaces are not filled in\r
528             PushRequired(pScanClient);\r
529             // we will exit the scan through the APIDescriptor of this client and look at other clients\r
530             break;\r
531           }\r
532         }\r
533       }\r
534     }  \r
535   }\r
536   return false;\r
537 }\r
538 \r
539 bool CSynapseServer::DoResolve(CSynapseClient *pClient)\r
540 {\r
541   list<CSynapseClientSlot>::iterator iSlot;\r
542   for(iSlot=mClients.begin(); iSlot != mClients.end(); iSlot++)\r
543   {\r
544     if ((*iSlot).mpClient == pClient)\r
545       break;\r
546   }\r
547   if (iSlot == mClients.end())\r
548   {\r
549     Syn_Printf("CSynapserServer::Resolve adding new client slot '%s'\n", pClient->GetInfo());\r
550     CSynapseClientSlot slot;\r
551     slot.mpClient = pClient;\r
552     slot.mFileName = "local client";\r
553     // make it active so we can request the interfaces already\r
554     pClient->ForceSetActive();\r
555     mClients.push_front(slot);\r
556   }\r
557   else\r
558   {\r
559     // make it active so we can request the interfaces already\r
560     (*iSlot).mpClient->ForceSetActive();\r
561   }\r
562 \r
563   // push the interfaces that need to be resolved for this client  \r
564   // NOTE: this doesn't take care of the SYN_REQUIRE_ANY interfaces\r
565   PushRequired(pClient);\r
566   // start resolving now\r
567   // working till the stack is emptied or till we reach a dead end situation\r
568   // we do a depth first traversal, we will grow the interface stack to be resolved till we start finding solutions\r
569   list<APIDescriptor_t*>::iterator iCurrent;\r
570   mbStackChanged = true; // init to true so we try the first elem\r
571   while (!mStack.empty())\r
572   {\r
573     //DumpStack();\r
574     if (!mbStackChanged)\r
575     {\r
576       // the stack didn't change last loop \r
577       iCurrent++;\r
578       if (iCurrent==mStack.end())\r
579       {\r
580         Syn_Printf("ERROR: CSynapseServer::Resolve, failed to resolve\n");\r
581         DumpStack();\r
582         return false;\r
583       }\r
584       if (ResolveAPI(*iCurrent))\r
585       {\r
586         iCurrent = mStack.erase(iCurrent);\r
587         mbStackChanged = true;\r
588       }\r
589     }\r
590     else\r
591     {\r
592       // the stack changed at last loop\r
593       mbStackChanged = false;\r
594       iCurrent = mStack.begin();\r
595       if (ResolveAPI(*iCurrent))\r
596       {\r
597         iCurrent = mStack.erase(iCurrent);\r
598         mbStackChanged = true;\r
599       }\r
600     }\r
601   }\r
602   return true;\r
603 }\r
604 \r
605 bool CSynapseServer::Resolve(CSynapseClient *pClient)\r
606 {\r
607   bool ret = DoResolve(pClient);\r
608   list<CSynapseClientSlot>::iterator iClient;  \r
609   iClient = mClients.begin();\r
610   while(iClient != mClients.end())\r
611   {\r
612     CSynapseClient *pClient = (*iClient).mpClient;\r
613     if (!pClient->IsActive())\r
614     {\r
615       Syn_Printf("Unloading an unused module: '%s'\n", pClient->GetInfo());\r
616       iClient = ShutdownClient(iClient);\r
617     }\r
618     else\r
619       iClient++;\r
620   }\r
621   return ret;\r
622 }\r
623 \r
624 void CSynapseServer::Shutdown()\r
625 {\r
626   Syn_Printf("Synapse server core is shutting down\n");\r
627   // do a first pass to shutdown the clients nicely (i.e. decref, release memory and drop everything)\r
628   // 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\r
629   list<CSynapseClientSlot>::iterator iClient;  \r
630   iClient = mClients.begin();\r
631   for(iClient = mClients.begin(); iClient != mClients.end(); iClient++)\r
632   {\r
633     (*iClient).mpClient->Shutdown();\r
634   }  \r
635   // now release them from the server's point of view\r
636   iClient = mClients.begin();\r
637   while(iClient != mClients.end())\r
638   {\r
639     iClient = ShutdownClient(iClient);\r
640   }  \r
641 }\r
642 \r
643 void CSynapseServer::DumpStack()\r
644 {\r
645   list<APIDescriptor_t*>::iterator iCurrent;\r
646   for(iCurrent=mStack.begin(); iCurrent != mStack.end(); iCurrent++)\r
647   {\r
648     APIDescriptor_t*pAPI = *iCurrent;\r
649     Syn_Printf("interface %s %p '%s' '%s'\n", APITypeName[pAPI->mType], pAPI, pAPI->major_name, pAPI->minor_name);\r
650   }\r
651 }\r
652 \r
653 void CSynapseServer::DumpActiveClients()\r
654 {\r
655   list<CSynapseClientSlot>::iterator iClient;  \r
656   for(iClient=mClients.begin(); iClient!=mClients.end(); iClient++)\r
657   {\r
658     CSynapseClient *pClient = (*iClient).mpClient;\r
659     Syn_Printf("%s", pClient->GetInfo());\r
660     if (pClient->IsActive())\r
661       Syn_Printf("\n");\r
662     else\r
663       Syn_Printf(" (not active)\n");\r
664   }\r
665 }\r
666 \r
667 bool CSynapseServer::SelectClientConfig(const char *client_name)\r
668 {\r
669   if (!mpDoc)\r
670     return false;\r
671   xmlNodePtr pNode = xmlDocGetRootElement(mpDoc);\r
672   if (!pNode)\r
673     return false;\r
674   // look for the client\r
675   pNode=pNode->children;\r
676   while(pNode)\r
677   {\r
678     if (pNode->type == XML_ELEMENT_NODE)\r
679     {\r
680       xmlChar *prop = xmlGetProp(pNode, (const xmlChar *)"name");\r
681       if (!strcmp((const char *)prop, client_name))\r
682       {\r
683         xmlFree(prop);\r
684         break;\r
685       }\r
686       xmlFree(prop);\r
687     }\r
688     pNode = pNode->next;\r
689   }\r
690   if (!pNode)\r
691     return false; // config you asked for isn't there\r
692   // focus\r
693   mpFocusedNode = pNode->children;\r
694   mpCurrentClientConfig = pNode;\r
695   return true;\r
696 }\r
697 \r
698 bool CSynapseServer::GetNextConfig(char **api_name, char **minor)\r
699 {\r
700   while(mpFocusedNode && mpFocusedNode->name)\r
701   {\r
702     if (mpFocusedNode->type == XML_ELEMENT_NODE && !strcmp((const char *)mpFocusedNode->name, "api"))\r
703     {\r
704       if (m_api_name)\r
705         xmlFree(m_api_name);\r
706       m_api_name = xmlGetProp(mpFocusedNode, (const xmlChar *)"name");\r
707       *api_name = (char *)m_api_name;\r
708       if (m_content)\r
709         g_free(m_content);\r
710       m_content = g_strdup((const gchar *)mpFocusedNode->children->content);\r
711       g_strstrip(m_content);\r
712       *minor = m_content;\r
713       mpFocusedNode = mpFocusedNode->next;\r
714       return true;\r
715     }\r
716     mpFocusedNode = mpFocusedNode->next;    \r
717   }\r
718   return false;\r
719 }\r
720 \r
721 bool CSynapseServer::GetConfigForAPI( const char *api, char **minor ) {\r
722   xmlNodePtr pNode = mpCurrentClientConfig->children;\r
723   while ( pNode && pNode->name ) {\r
724     if ( pNode->type == XML_ELEMENT_NODE && !strcmp( (const char *)pNode->name, "api" ) ) {\r
725       if ( m_api_name ) {\r
726         xmlFree( m_api_name );\r
727       }\r
728       m_api_name = xmlGetProp( pNode, (const xmlChar *)"name" );\r
729       if ( !strcmp( (const char *)m_api_name, api ) ) {\r
730         if ( m_content ) {\r
731           g_free( m_content );\r
732         }\r
733         m_content = g_strdup( (const gchar *)pNode->children->content );\r
734         g_strstrip( m_content );\r
735         *minor = m_content;\r
736         return true;\r
737       }\r
738     }\r
739     pNode = pNode->next;\r
740   }\r
741   return false;\r
742 }\r
743 \r
744 const char *CSynapseServer::GetModuleFilename(CSynapseClient *pClient)\r
745 {\r
746   list<CSynapseClientSlot>::iterator iSlot;\r
747   for(iSlot=mClients.begin(); iSlot != mClients.end(); iSlot++)\r
748   {\r
749     if ((*iSlot).mpClient == pClient)\r
750     {\r
751       if ((*iSlot).mType == SYN_BUILTIN)\r
752       {\r
753         return "";      // FIXME\r
754       }\r
755       else\r
756       {\r
757         return (*iSlot).mFileName;\r
758       }\r
759     }\r
760   }\r
761   return NULL;\r
762 }\r
763 \r
764 /*\r
765 =======================================================================\r
766 client\r
767 =======================================================================\r
768 */\r
769 \r
770 CSynapseClient::CSynapseClient()\r
771 {\r
772 }\r
773 \r
774 void CSynapseClient::Shutdown()\r
775 {\r
776   vector<APIDescriptor_t *>::iterator iAPI;\r
777   for(iAPI=mAPIDescriptors.begin(); iAPI!=mAPIDescriptors.end(); iAPI++)\r
778   {\r
779     APIDescriptor_t *pAPI = *iAPI;\r
780     if (pAPI->mRefCount != 0)\r
781       Syn_Printf("WARNING: ~CSynapseClient '%s' has non-zero ref count for interface '%s' '%s'\n", GetInfo(), pAPI->major_name, pAPI->minor_name);\r
782     else\r
783       delete pAPI;\r
784     *iAPI=NULL;\r
785   }\r
786   mAPIDescriptors.clear();\r
787   vector<CSynapseAPIManager *>::iterator iManager;\r
788   for(iManager=mManagersList.begin(); iManager!=mManagersList.end(); iManager++)\r
789   {\r
790     CSynapseAPIManager *pManager = *iManager;\r
791     pManager->DecRef();\r
792     *iManager = NULL;\r
793   }\r
794   mManagersList.clear();\r
795   for(iManager=mManagersMatch.begin(); iManager!=mManagersMatch.end(); iManager++)\r
796   {\r
797     CSynapseAPIManager *pManager = *iManager;\r
798     pManager->DecRef();\r
799     *iManager = NULL;\r
800   }\r
801   mManagersMatch.clear();\r
802 }\r
803 \r
804 CSynapseClient::~CSynapseClient()\r
805 {\r
806   // this should not be doing anything when called from here if everything went right\r
807   // otherwise it's likely to crash .. at least that's the sign we missed something\r
808   Shutdown();\r
809 }\r
810 \r
811 int CSynapseClient::GetAPICount()\r
812 {\r
813   return mAPIDescriptors.size();\r
814 }\r
815 \r
816 APIDescriptor_t* CSynapseClient::GetAPIDescriptor(int i)\r
817 {\r
818   return mAPIDescriptors[i];\r
819 }\r
820 \r
821 int CSynapseClient::GetManagerMatchCount()\r
822 {\r
823   return mManagersMatch.size();\r
824 }\r
825 \r
826 CSynapseAPIManager* CSynapseClient::GetManagerMatch(int i)\r
827 {\r
828   return mManagersMatch[i];\r
829 }\r
830 \r
831 int CSynapseClient::GetManagerListCount()\r
832 {\r
833   return mManagersList.size();\r
834 }\r
835 \r
836 CSynapseAPIManager* CSynapseClient::GetManagerList(int i)\r
837 {\r
838   return mManagersList[i];\r
839 }\r
840 \r
841 bool CSynapseClient::AddAPI(const char *major, const char *minor, int size, EAPIType type, void *pTable)\r
842 {\r
843   // do some safe checks before actual addition\r
844   if (type == SYN_REQUIRE && !pTable)\r
845   {\r
846     Syn_Printf("ERROR: interface '%s' '%s' from '%s' is SYN_REQUIRE and doesn't provide a function table pointer\n", major, minor, GetInfo());\r
847     return false;\r
848   }\r
849   if (pTable)\r
850   {\r
851     int *pi = (int *)pTable;\r
852     if (pi == 0)\r
853     {\r
854       Syn_Printf("ERROR: forgot to init function table size for interface '%s' '%s' from '%s'?\n", major, minor, GetInfo());\r
855       return false;\r
856     }\r
857   }\r
858   APIDescriptor_t *pAPI = new APIDescriptor_t;\r
859   memset(pAPI, 0, sizeof(APIDescriptor_t));\r
860   strncpy(pAPI->major_name, major, MAX_APINAME);\r
861   if (minor)\r
862     strncpy(pAPI->minor_name, minor, MAX_APINAME);\r
863   pAPI->mType = type;\r
864   pAPI->mpTable = pTable;\r
865   // store the interface size\r
866   if (type == SYN_PROVIDE)\r
867   {\r
868     if (size == 0)\r
869     {\r
870       Syn_Printf("ERROR: size of the interface required for a SYN_PROVIDE ('%s' '%s' from '%s')\n", major, minor, GetInfo());\r
871       delete pAPI;\r
872       return false;\r
873     }\r
874     pAPI->mSize = size;\r
875   }\r
876   else if (type == SYN_REQUIRE)\r
877   {    \r
878     if (size != 0)\r
879     {\r
880       // if a non-zero value is given in function call, use this instead of the val in table\r
881       *((int *)pAPI->mpTable) = size;\r
882       pAPI->mSize = size;\r
883     }\r
884     else\r
885     {\r
886       pAPI->mSize = *((int *)pAPI->mpTable);\r
887       if (pAPI->mSize == 0)\r
888       {\r
889         Syn_Printf("ERROR: didn't get an interface size ('%s' '%s' from '%s')\n", major, minor, GetInfo());\r
890         delete pAPI;\r
891         return false;\r
892       }\r
893     }\r
894   }\r
895   else\r
896   {\r
897     Syn_Printf("ERROR: AddAPI type '%d' not supported\n", type);\r
898     return false;\r
899   }\r
900   mAPIDescriptors.push_back(pAPI);  \r
901   #ifdef SYNAPSE_VERBOSE\r
902     Syn_Printf("AddAPI: %s %p '%s' '%s' from '%s', size %d\n", APITypeName[pAPI->mType], pAPI, major, minor, GetInfo(), pAPI->mSize);\r
903   #endif\r
904   return true;\r
905 }\r
906 \r
907 #include "version.h"\r
908 \r
909 const char* CSynapseClient::GetInfo()\r
910 {\r
911   return "CSynapseClient built " __DATE__ " " RADIANT_VERSION;\r
912 }\r
913 \r
914 bool CSynapseClient::CheckSetActive()\r
915 {\r
916   if (mbActive)\r
917     return true;\r
918   int i,max=GetAPICount();\r
919   for(i=0; i<max; i++)\r
920   {\r
921     APIDescriptor_t *pAPI = GetAPIDescriptor(i);\r
922     if (pAPI->mType == SYN_REQUIRE && !pAPI->mbTableInitDone)\r
923       return false;\r
924   }\r
925   // if we have managers with fixed list, those need to be completely filled in too\r
926   vector<CSynapseAPIManager *>::iterator iManager;\r
927   for(iManager=mManagersList.begin(); iManager!=mManagersList.end(); iManager++)\r
928   {\r
929     if (!(*iManager)->CheckSetActive())\r
930       return false; // one of the managers doesn't have all it needs yet\r
931   }\r
932   // call OnActivate to let the client perform last minute checks\r
933   // NOTE: this should be fatal instead of letting the engine try other combinations\r
934   if (!OnActivate()) {\r
935     return false;\r
936   }\r
937   // yes, all required interfaces have been initialized\r
938   Syn_Printf("'%s' activated\n", GetInfo());\r
939   mbActive = true;\r
940   return true;\r
941 }\r
942 \r
943 bool CSynapseClient::ConfigXML( CSynapseServer *pServer, const char *client_name, const XMLConfigEntry_t entries[] ) {\r
944   \r
945   if ( !client_name ) {\r
946     client_name = GetName();\r
947   }\r
948   \r
949   Syn_Printf("Dynamic APIs for client '%s'\n", GetInfo());\r
950   if ( !pServer->SelectClientConfig( client_name ) )\r
951   {\r
952     Syn_Printf( "Failed to select synapse client config '%s'\n", client_name );\r
953     return false;\r
954   }\r
955   \r
956   int i = 0;\r
957   while ( entries[i].type != SYN_UNKNOWN ) { // don't test pTable, for a SYN_PROVIDE it will be empty\r
958     char *minor;\r
959     if ( !pServer->GetConfigForAPI( entries[i].api, &minor ) ) {\r
960       Syn_Printf( "GetConfigForAPI '%s' failed - invalid XML config file?\n", entries[i].api );\r
961       return false;\r
962     }\r
963     AddAPI( entries[i].api, minor, entries[i].size, entries[i].type, entries[i].pTable );\r
964     i++;\r
965   }\r
966   Syn_Printf( "%d dynamic interfaces parsed for '%s'\n", i, client_name );\r
967   return true;\r
968 }\r
969 \r
970 void CSynapseClient::AddManager(CSynapseAPIManager *pManager)\r
971 {\r
972   pManager->IncRef();\r
973   if (pManager->GetType() == API_LIST)\r
974     mManagersList.push_back(pManager);\r
975   else\r
976     mManagersMatch.push_back(pManager);\r
977 }\r
978 \r
979 CSynapseAPIManager::~CSynapseAPIManager()\r
980 {\r
981   vector<APIDescriptor_t *>::iterator iAPI;\r
982   for(iAPI=mAPIs.begin(); iAPI!=mAPIs.end(); iAPI++)\r
983   {\r
984     APIDescriptor_t *pAPI = *iAPI;\r
985     if (pAPI->mRefCount != 0)\r
986     {\r
987       Syn_Printf("WARNING: ~CSynapseAPIManager has non-zero ref count for interface '%s' '%s'\n", pAPI->major_name, pAPI->minor_name);\r
988     }\r
989     delete pAPI;\r
990     *iAPI = NULL;\r
991   }\r
992 }\r
993 \r
994 APIDescriptor_t* CSynapseAPIManager::PrepareRequireAPI(APIDescriptor_t *pAPI)\r
995 {\r
996 #ifdef _DEBUG\r
997   if (pAPI->mType != SYN_PROVIDE)\r
998   {\r
999     Syn_Printf("ERROR: unexpected pAPI->mType != SYN_PROVIDE in CSynapseAPIManager::PrepareRequireAPI\n");\r
1000     return NULL;\r
1001   }\r
1002 #endif\r
1003   APIDescriptor_t *pRequireAPI = new APIDescriptor_t;\r
1004   memcpy(pRequireAPI, pAPI, sizeof(APIDescriptor_t));\r
1005   pRequireAPI->mType = SYN_REQUIRE_ANY;\r
1006   pRequireAPI->mpTable = NULL;\r
1007   pRequireAPI->mbTableInitDone = false;\r
1008   pRequireAPI->mSize = 0; // this will have to be set correctly by the child for version checking\r
1009   pRequireAPI->mRefCount = 0;\r
1010   return pRequireAPI;\r
1011 }\r
1012 \r
1013 void CSynapseAPIManager::SetMatchAPI(const char *major, const char *minor)\r
1014 {\r
1015   if (strlen(minor)>MAX_PATTERN_STRING)\r
1016   {\r
1017     Syn_Printf("ERROR: MAX_TOKEN_STRING exceeded in CSynapseAPIManager::SetMatchAPI: '%s'\n", minor);\r
1018     return;\r
1019   }\r
1020   strcpy(major_pattern, major);\r
1021   strcpy(minor_pattern, minor);\r
1022   if (strcmp(minor, "*"))\r
1023   {\r
1024     mType = API_LIST;\r
1025   }\r
1026 }\r
1027 \r
1028 bool CSynapseAPIManager::MatchAPI(const char *major, const char *minor)\r
1029 {\r
1030   assert(mType == API_MATCH);\r
1031   \r
1032   /*!\r
1033   if this interface has been allocated already, avoid requesting it again..\r
1034   */\r
1035   vector<APIDescriptor_t *>::iterator iAPI;\r
1036   for(iAPI=mAPIs.begin(); iAPI!=mAPIs.end(); iAPI++)\r
1037   {\r
1038     if (CSynapseServer::MatchAPI((*iAPI)->major_name, (*iAPI)->minor_name, major, minor))\r
1039       return false;\r
1040   }\r
1041   \r
1042   if (!strcmp(major, major_pattern))\r
1043     return true;\r
1044   return false;\r
1045 }\r
1046 \r
1047 bool CSynapseAPIManager::CheckSetActive()\r
1048 {\r
1049   if (mType == API_MATCH)\r
1050     return false;\r
1051   // mType == API_LIST\r
1052   int i,max = GetAPICount();\r
1053   for(i=0; i<max; i++)\r
1054   {\r
1055     if (!GetAPI(i)->mbTableInitDone)\r
1056       return false;\r
1057   }\r
1058   return true;\r
1059 }\r
1060 \r
1061 void CSynapseAPIManager::InitializeAPIList()\r
1062 {\r
1063   char minor_tok[MAX_PATTERN_STRING];\r
1064   char *token;\r
1065   \r
1066   if (mAPIs.size())\r
1067   {\r
1068     Syn_Printf("WARNING: CSynapseAPIManager::InitializeAPIList on an already initialized APIManager\n");\r
1069     return;\r
1070   }\r
1071   \r
1072   strncpy(minor_tok, minor_pattern, MAX_PATTERN_STRING);\r
1073   token = strtok(minor_tok, " ");\r
1074   while (token)\r
1075   {\r
1076     /* ask the child to build from scratch */\r
1077     APIDescriptor_t *pAPI = new APIDescriptor_t;\r
1078     memset(pAPI, 0, sizeof(APIDescriptor_t));\r
1079     strncpy(pAPI->major_name, major_pattern, MAX_APINAME);\r
1080     strncpy(pAPI->minor_name, token, MAX_APINAME);\r
1081     pAPI->mType = SYN_REQUIRE_ANY;\r
1082     FillAPITable(pAPI);\r
1083     mAPIs.push_back(pAPI);\r
1084     token = strtok(NULL, " ");\r
1085   }\r
1086 }\r
1087   \r
1088 int CSynapseAPIManager::GetAPICount()\r
1089 {\r
1090   return mAPIs.size();\r
1091 }\r
1092 \r
1093 APIDescriptor_t* CSynapseAPIManager::GetAPI(int i)\r
1094 {\r
1095   return mAPIs[i];\r
1096 }\r
1097 \r
1098 // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=879\r
1099 void fini_stub() {\r
1100   printf( "fini_stub\n" );\r
1101 }\r