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