Salome HOME
Issue #329: avoid app crashes on malformed python feature launching
[modules/shaper.git] / src / Config / Config_ModuleReader.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 /*
4  * Config_ModuleReader.cpp
5  *
6  *  Created on: Mar 20, 2014
7  *      Author: sbh
8  */
9
10 #include <Config_Keywords.h>
11 #include <Config_Common.h>
12 #include <Config_ModuleReader.h>
13 #include <Config_FeatureReader.h>
14 #include <Events_Error.h>
15
16 #include <libxml/parser.h>
17 #include <libxml/tree.h>
18
19 // Have to be included before std headers
20 #include <Python.h>
21
22 //Necessary for cerr
23 #include <iostream>
24 #include <algorithm>
25
26 #ifdef WIN32
27 #include <windows.h>
28 #pragma warning(disable : 4996) // for getenv
29 #else
30 #include <dlfcn.h>
31 #endif
32
33 std::map<std::string, Config_ModuleReader::PluginType> Config_ModuleReader::myPluginTypes;
34
35 Config_ModuleReader::Config_ModuleReader(const char* theEventGenerated)
36     : Config_XMLReader(PLUGIN_FILE),
37       myEventGenerated(theEventGenerated)
38 {
39   myHaveSalome = false;
40   char* anEnv = getenv("SALOME_ROOT_DIR");
41   std::string value = normalize(anEnv);
42   if (!value.empty()) {
43     myHaveSalome = true;
44   }
45
46 }
47
48 Config_ModuleReader::~Config_ModuleReader()
49 {
50 }
51
52 const std::map<std::string, std::string>& Config_ModuleReader::featuresInFiles() const
53 {
54   return myFeaturesInFiles;
55 }
56
57 /*
58  * Get module name from plugins.xml
59  * (property "module")
60  */
61 std::string Config_ModuleReader::getModuleName()
62 {
63   xmlNodePtr aRoot = findRoot();
64   return getProperty(aRoot, PLUGINS_MODULE);
65 }
66
67 /*
68  *
69  */
70 void Config_ModuleReader::processNode(xmlNodePtr theNode)
71 {
72   if (isNode(theNode, NODE_PLUGIN, NULL)) {
73     bool isAvailable = isAvaliableOnThisPlatform(getProperty(theNode, PLUGIN_PLATFORM));
74     if (!isAvailable)
75       return;
76     std::string aPluginConf = getProperty(theNode, PLUGIN_CONFIG);
77     std::string aPluginLibrary = getProperty(theNode, PLUGIN_LIBRARY);
78     std::string aPluginScript = getProperty(theNode, PLUGIN_SCRIPT);
79     std::string aPluginName = addPlugin(aPluginLibrary, aPluginScript, aPluginConf);
80
81     std::list<std::string> aFeatures = importPlugin(aPluginName, aPluginConf);
82     std::list<std::string>::iterator it = aFeatures.begin();
83     for (; it != aFeatures.end(); it++) {
84       myFeaturesInFiles[*it] = aPluginConf;
85     }
86   }
87 }
88
89 bool Config_ModuleReader::processChildren(xmlNodePtr theNode)
90 {
91   return isNode(theNode, NODE_PLUGINS, NULL);
92 }
93
94 std::list<std::string> Config_ModuleReader::importPlugin(const std::string& thePluginLibrary,
95                                                          const std::string& thePluginXmlConf)
96 {
97   if (thePluginXmlConf.empty()) {  //probably a third party library
98     loadLibrary(thePluginLibrary);
99     return std::list<std::string>();
100   }
101
102   Config_FeatureReader aReader = Config_FeatureReader(thePluginXmlConf,
103                                                       thePluginLibrary,
104                                                       myEventGenerated);
105   aReader.readAll();
106   return aReader.features();
107 }
108
109 std::string Config_ModuleReader::addPlugin(const std::string& aPluginLibrary,
110                                            const std::string& aPluginScript,
111                                            const std::string& aPluginConf)
112 {
113   PluginType aType = Config_ModuleReader::Binary;
114   std::string aPluginName;
115   if (!aPluginLibrary.empty()) {
116     aPluginName = aPluginLibrary;
117     if (aPluginConf.empty()) {
118       aType = Config_ModuleReader::Intrenal;
119     }
120   } else if (!aPluginScript.empty()) {
121     aPluginName = aPluginScript;
122     aType = Config_ModuleReader::Python;
123   }
124   if(!aPluginName.empty()) {
125     myPluginTypes[aPluginName] = aType;
126   }
127
128   return aPluginName;
129 }
130
131 void Config_ModuleReader::loadPlugin(const std::string thePluginName)
132 {
133   PluginType aType = Config_ModuleReader::Binary;
134   if(myPluginTypes.find(thePluginName) != myPluginTypes.end()) {
135     aType = myPluginTypes.at(thePluginName);
136   }
137   switch (aType) {
138     case Config_ModuleReader::Python:
139       loadScript(thePluginName);
140       break;
141     case Config_ModuleReader::Binary:
142     case Config_ModuleReader::Intrenal:
143     default:
144       loadLibrary(thePluginName);
145       break;
146   }
147 }
148
149 bool Config_ModuleReader::isAvaliableOnThisPlatform(const std::string& thePluginPlatform)
150 {
151   bool result = true;
152   PluginPlatform aPlatform = All;
153   std::string aPlatformName = normalize(thePluginPlatform) ;
154   if (aPlatformName == PLUGIN_PLATFORM_SALOME) {
155     aPlatform = Salome;
156   } else if (aPlatformName == PLUGIN_PLATFORM_NEWGEOM) {
157     aPlatform = OpenParts;
158   } else if (!thePluginPlatform.empty()) {
159     Events_Error::send("Unknown platform: " + thePluginPlatform);
160   }
161   if (aPlatform == All) {
162     result = true;
163   } else if (myHaveSalome) {
164     result = aPlatform == Salome;
165   } else {
166     result = aPlatform == OpenParts;
167   }
168   return result;
169
170 }
171
172 void Config_ModuleReader::loadScript(const std::string theFileName)
173 {
174   /* aquire python thread */
175   PyGILState_STATE gstate = PyGILState_Ensure();
176
177   PyObject* module = PyImport_ImportModule(theFileName.c_str());
178   if (!module) {
179     std::string anErrorMsg = "An error occured while importing " + theFileName;
180     //Get detailed error message:
181     if (PyErr_Occurred()) {
182       PyObject *pstr, *ptype, *pvalue, *ptraceback;
183       PyErr_Fetch(&ptype, &pvalue, &ptraceback);
184       PyErr_NormalizeException(&ptype, &pvalue, &ptraceback);
185       pstr = PyObject_Str(pvalue);
186       std::string aPyError = std::string(PyString_AsString(pstr));
187       if (!aPyError.empty()) {
188         anErrorMsg += ":\n" + aPyError;
189       }
190       Py_XDECREF(pstr);
191       Py_XDECREF(ptype);
192       Py_XDECREF(pvalue);
193       Py_XDECREF(ptraceback);
194     }
195     Events_Error::send(anErrorMsg);
196   }
197
198   /* release python thread */
199   PyGILState_Release(gstate);
200 }
201
202 void Config_ModuleReader::loadLibrary(const std::string theLibName)
203 {
204   std::string aFileName = library(theLibName);
205   if (aFileName.empty())
206     return;
207
208 #ifdef WIN32
209   HINSTANCE aModLib = ::LoadLibrary(aFileName.c_str());
210 #else
211   void* aModLib = dlopen( aFileName.c_str(), RTLD_LAZY | RTLD_GLOBAL );
212 #endif
213   if(!aModLib && theLibName != "DFBrowser") { // don't show error for internal debugging tool
214     std::string anErrorMsg = "Failed to load " + aFileName;
215     #ifndef WIN32
216     anErrorMsg += ": " + std::string(dlerror());
217     #endif
218     Events_Error::send(anErrorMsg);
219   }
220 }
221