1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */
2 
3 /* AbiSource Application Framework
4  * Copyright (C) 2001 AbiSource, Inc.
5  * Copyright (C) 2001 Dom Lachowicz <cinamod@hotmail.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  * 02110-1301 USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include "ut_exception.h"
28 #include "ut_vector.h"
29 #include "ut_assert.h"
30 #include "ut_debugmsg.h"
31 
32 #include "xap_ModuleManager.h"
33 #include "xap_App.h"
34 #include "xap_Prefs.h"
35 #include "ut_string_class.h"
36 #include "ut_path.h"
37 
38 // the loader manages instances of one of these target classes
39 
40 #if defined (TOOLKIT_WIN)
41   #include "xap_Win32Module.h"
42   #define MODULE_CLASS XAP_Win32Module
43 #elif defined (TOOLKIT_COCOA)
44   #include "xap_CocoaModule.h"
45   #define MODULE_CLASS XAP_CocoaModule
46 #else
47   #include "xap_UnixModule.h"
48   #define MODULE_CLASS XAP_UnixModule
49 #endif
50 
51 // log information about plugin loading into the <log> section of AbiWord.profile
52 // (we save the prefs file after each call, so as to maximise the information we have in
53 // case a plugin crashes and the crash handler does not get a chance to save it for us
54 #define XAP_MODULE_MANAGER_LOAD_LOG(msg1, msg2)                                      \
55 if(XAP_App::getApp() && XAP_App::getApp()->getPrefs())                               \
56 {                                                                                    \
57     UT_String __s;                                                                   \
58     UT_String_sprintf(__s, "(L%d): %s %s", __LINE__, msg1, msg2);                    \
59 	UT_DEBUGMSG(("%s\n",__s.c_str()));                                                 \
60     XAP_App::getApp()->getPrefs()->log("XAP_ModuleManager::loadModule", __s.c_str());\
61 	XAP_App::getApp()->getPrefs()->savePrefsFile();                                  \
62 }
63 
64 /*!
65  * Protected destructor creates an instance of this module class
66  */
XAP_ModuleManager()67 XAP_ModuleManager::XAP_ModuleManager ()
68 {
69 	m_modules = new UT_GenericVector<XAP_Module*> (11);
70 }
71 
72 /*!
73  * Private destructor
74  */
~XAP_ModuleManager()75 XAP_ModuleManager::~XAP_ModuleManager ()
76 {
77 	UT_VECTOR_PURGEALL (MODULE_CLASS *, (*m_modules));
78 	delete m_modules;
79 }
80 
81 /*!
82  * Acquire a handle to an instance of this module loader class
83  */
instance()84 XAP_ModuleManager & XAP_ModuleManager::instance ()
85 {
86 	static XAP_ModuleManager me;
87 	return me;
88 }
89 
90 /*!
91  * Request that the ModuleManager load the module represented by szFilename.
92  *
93  * \param szFilename - the .dll or .so on your system that you wish to load
94  *
95  * \return true if loaded successfully, false otherwise
96  */
loadModule(const char * szFilename)97 bool XAP_ModuleManager::loadModule (const char * szFilename)
98 {
99 	UT_ASSERT (szFilename);
100 
101 	if ( szFilename == 0) return false;
102 	if (*szFilename == 0) return false;
103 
104 	XAP_MODULE_MANAGER_LOAD_LOG("loading", szFilename)
105 
106 	// check to see if plugin is already loaded
107 
108 	XAP_Module* pModuleLoop = 0;
109 	const UT_GenericVector<class XAP_Module *> *pVec = enumModules();
110 
111 	for (UT_sint32 i = 0; i < pVec->size(); i++)
112 	{
113 		pModuleLoop = (XAP_Module *)pVec->getNthItem (i);
114 
115 		char * moduleName = 0;
116 		if(pModuleLoop && pModuleLoop->getModuleName(&moduleName))
117 		{
118 			if (!strcmp(UT_basename(szFilename), UT_basename(moduleName)))
119 			{
120 				// already loaded, don't attempt to load again and exit quietly
121 				FREEP(moduleName);
122 				return true;
123 			}
124 			FREEP(moduleName);
125 		}
126 	}
127 
128 
129 	XAP_Module * pModule = 0;
130 	UT_TRY
131 	{
132 		pModule = new MODULE_CLASS;
133 	}
134 	UT_CATCH (...)
135 	{
136 		pModule = 0;
137 	}
138 	if (pModule == 0) return false;
139 
140 	if (!pModule->load (szFilename))
141 	{
142 		UT_DEBUGMSG (("Failed to load module %s\n", szFilename));
143 		XAP_MODULE_MANAGER_LOAD_LOG("failed to load", szFilename)
144 
145 		char * errorMsg = 0;
146 		if (pModule->getErrorMsg (&errorMsg))
147 		{
148 			UT_DEBUGMSG (("Reason: %s\n", errorMsg));
149 			XAP_MODULE_MANAGER_LOAD_LOG("error msg", errorMsg)
150 			FREEP (errorMsg);
151 		}
152 		delete pModule;
153 		return false;
154 	}
155 
156 	/* assign the module's creator to be us, etc.
157 	 */
158 	pModule->setLoaded (true);
159 	pModule->setCreator (this);
160 
161 	if (!pModule->registerThySelf ())
162 	{
163 		UT_DEBUGMSG (("Failed to register module %s\n", szFilename));
164 		XAP_MODULE_MANAGER_LOAD_LOG("failed to register", szFilename)
165 
166 		char * errorMsg = 0;
167 		if (pModule->getErrorMsg (&errorMsg))
168 		{
169 			UT_DEBUGMSG (("Reason: %s\n", errorMsg?errorMsg:"unknown"));
170 			XAP_MODULE_MANAGER_LOAD_LOG("error msg",
171 errorMsg?errorMsg:"Unknown")
172 			FREEP (errorMsg);
173 		}
174 		pModule->unload ();
175 		delete pModule;
176 		return false;
177 	}
178 	if (m_modules->addItem (pModule)) // an error occurred...
179 	{
180 		XAP_MODULE_MANAGER_LOAD_LOG("could not add", szFilename)
181 		pModule->unregisterThySelf ();
182 		pModule->unload ();
183 		delete pModule;
184 		return false;
185 	}
186 
187 	/* we (somehow :^) got here. count our blessings and return
188 	 */
189 	XAP_MODULE_MANAGER_LOAD_LOG("success", szFilename)
190 	return true;
191 }
192 
193 /*!
194 * Load builtin plugins.
195 */
loadPreloaded(XAP_Plugin_Registration fnRegister,XAP_Plugin_Registration fnDeregister,XAP_Plugin_VersionCheck fnSupportsVersion)196 bool XAP_ModuleManager::loadPreloaded (XAP_Plugin_Registration fnRegister,
197 									   XAP_Plugin_Registration fnDeregister,
198 									   XAP_Plugin_VersionCheck fnSupportsVersion)
199 {
200 	UT_ASSERT (fnRegister && fnDeregister && fnSupportsVersion);
201 
202 	if (!(fnRegister && fnDeregister && fnSupportsVersion)) return false;
203 
204 	XAP_Module * pModule = 0;
205 	UT_TRY
206 		{
207 			pModule = new MODULE_CLASS;
208 		}
209 	UT_CATCH (...)
210 		{
211 			pModule = 0;
212 		}
213 	if (pModule == 0) return false;
214 
215 	if (!pModule->setSymbols (fnRegister, fnDeregister, fnSupportsVersion)) // huh?
216 		{
217 			delete pModule;
218 			return false;
219 		}
220 
221 	/* assign the module's creator to be us, etc.
222 	 */
223 	pModule->setLoaded (true);
224 	pModule->setCreator (this);
225 
226 	if (!pModule->registerThySelf ())
227 		{
228 			UT_DEBUGMSG (("Failed to register preloaded module\n"));
229 			delete pModule;
230 			return false;
231 		}
232 	if (m_modules->addItem (pModule)) // an error occurred...
233 		{
234 			UT_DEBUGMSG (("oops! out of memory?\n"));
235 			pModule->unregisterThySelf ();
236 			delete pModule;
237 			return false;
238 		}
239 
240 	/* we (somehow :^) got here. count our blessings and return
241 	 */
242 	return true;
243 }
244 
245 /*!
246  * Unload a module
247  *
248  * \param pModule - a valid module that you want to unload
249  *
250  * WARNING: zero or more plugins/modules may be unloaded as a result of
251  * calling XAP_ModuleManager::unloadModule since the plugin may be unloaded
252  * already or other plugins may depend on this one and be unloaded
253  * automatically.
254  *
255  * Don't assume anything, therefore, about the UT_Vector returned by
256  * XAP_ModuleManager::enumModules
257  */
unloadModule(XAP_Module * pModule)258 void XAP_ModuleManager::unloadModule (XAP_Module * pModule)
259 {
260 	UT_ASSERT (pModule);
261 	if (pModule == 0) return;
262 
263 	UT_ASSERT (pModule->getCreator () == this);
264 	if (pModule->getCreator () != this) return;
265 
266 	UT_sint32 ndx = m_modules->findItem (pModule);
267 	if (ndx == -1)
268 	{
269 		UT_ASSERT (UT_SHOULD_NOT_HAPPEN);
270 		UT_DEBUGMSG (("Could not unload module\n"));
271 		return;
272 	}
273 	unloadModule (ndx);
274 }
275 
276 /* for use by unloadAllPlugins, unloadModule only!
277  */
unloadModule(UT_sint32 ndx)278 void XAP_ModuleManager::unloadModule (UT_sint32 ndx)
279 {
280 	UT_return_if_fail(m_modules != NULL);
281 
282 	XAP_Module * pModule = m_modules->getNthItem (ndx);
283 
284 	m_modules->deleteNthItem (ndx);
285 
286 	// we're less picky when unloading than we are when loading
287 	// the (necessarily true) assumptions are that
288 	//
289 	// 1) we were the one who loaded the module
290 	// 2) registerThySelf() worked
291 	//
292 	// so it had better damn well work in the opposite direction!
293 
294 	pModule->unregisterThySelf ();
295 	pModule->setLoaded (false);
296 
297 	bool module_unloaded = pModule->unload ();
298 	UT_UNUSED (module_unloaded);
299 	UT_ASSERT (module_unloaded);
300 
301 	delete pModule;
302 }
303 
304 /*!
305  * Enumerate the modules loaded thus far
306  *
307  * \return a vector containing XAP_Module*'s
308  */
enumModules() const309 const UT_GenericVector<XAP_Module*> * XAP_ModuleManager::enumModules () const
310 {
311 	// TODO: perhaps we should clone this
312 	return m_modules;
313 }
314 
315 /*!
316  * Unload all currently loaded plugins, in order of last added to first
317  *
318  * \return nothing
319  */
unloadAllPlugins()320 void XAP_ModuleManager::unloadAllPlugins ()
321 {
322 	UT_return_if_fail(m_modules != NULL);
323 
324 	/* make sure all the plugins are unloaded (reverse order loaded)
325 	 *
326 	 * Note: can no longer assume that XAP_ModuleManager::unloadModule()
327 	 *       will unload only one module.
328 	 */
329 	while (UT_sint32 count = m_modules->getItemCount ())
330 	{
331 		unloadModule (count - 1);
332 
333 		if (m_modules->getItemCount () == count) // huh?
334 		{
335 			UT_ASSERT (UT_SHOULD_NOT_HAPPEN);
336 			break;
337 		}
338 	}
339 }
340