1 /*
2 * dynamicloader.cpp
3 *
4 * This file is part of NEST.
5 *
6 * Copyright (C) 2004 The NEST Initiative
7 *
8 * NEST is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * NEST is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with NEST. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23 /*
24 This file is part of NEST.
25
26 dynamicloader.cpp -- Implements the class DynamicLoaderModule
27 to allow for dymanically loaded modules for extending the kernel.
28
29 Author(s):
30 Moritz Helias
31
32 First Version: November 2005
33 */
34
35 #include "dynamicloader.h"
36
37 #ifdef HAVE_LIBLTDL
38
39 // External includes:
40 #include <ltdl.h>
41
42 // Includes from libnestutil:
43 #include "logging.h"
44
45 // Includes from nestkernel:
46 #include "kernel_manager.h"
47 #include "model.h"
48
49 // Includes from sli:
50 #include "integerdatum.h"
51 #include "interpret.h"
52 #include "stringdatum.h"
53
54
55 namespace nest
56 {
57
58 struct sDynModule
59 {
60 std::string name;
61 lt_dlhandle handle;
62 SLIModule* pModule;
63
operator ==nest::sDynModule64 bool operator==( const sDynModule& rhs ) const
65 {
66 return name == rhs.name;
67 }
68
69 // operator!= must be implemented explicitly, not all compilers
70 // generate it automatically from operator==
operator !=nest::sDynModule71 bool operator!=( const sDynModule& rhs ) const
72 {
73 return not( *this == rhs );
74 }
75 };
76
77 // static member initialization
78 Dictionary* DynamicLoaderModule::moduledict_ = new Dictionary();
79
80 vecLinkedModules&
getLinkedModules()81 DynamicLoaderModule::getLinkedModules()
82 {
83 static vecLinkedModules lm; // initialized empty on first call
84 return lm;
85 }
86
87
88 /*! At the time when DynamicLoaderModule is constructed, the SLI Interpreter
89 and NestModule must be already constructed and initialized.
90 DynamicLoaderModule relies on the presence of
91 the following SLI datastructures: Name, Dictionary.
92 */
DynamicLoaderModule(SLIInterpreter & interpreter)93 DynamicLoaderModule::DynamicLoaderModule( SLIInterpreter& interpreter )
94 : loadmodule_function( dyn_modules )
95 {
96 interpreter.def( "moduledict", new DictionaryDatum( moduledict_ ) );
97 }
98
~DynamicLoaderModule()99 DynamicLoaderModule::~DynamicLoaderModule()
100 {
101 // unload all loaded modules
102 for ( vecDynModules::iterator it = dyn_modules.begin(); it != dyn_modules.end(); ++it )
103 {
104 if ( it->handle != NULL )
105 {
106 lt_dlclose( it->handle );
107 it->handle = NULL;
108 }
109 }
110
111 lt_dlexit();
112 }
113
114 // The following concerns the new module: -----------------------
115
116 const std::string
name(void) const117 DynamicLoaderModule::name( void ) const
118 {
119 return std::string( "NEST-Dynamic Loader" ); // Return name of the module
120 }
121
122 const std::string
commandstring(void) const123 DynamicLoaderModule::commandstring( void ) const
124 {
125 return std::string( "" ); // Run associated SLI startup script
126 }
127
128
129 // auxiliary function to check name of module via its pointer
130 // we cannot use a & for the second argument, as std::bind2nd() then
131 // becomes confused, at least with g++ 4.0.1.
132 bool
has_name(SLIModule const * const m,const std::string n)133 has_name( SLIModule const* const m, const std::string n )
134 {
135 return m->name() == n;
136 }
137
138
139 /** @BeginDocumentation
140 Name: Install - Load a dynamic module to extend the functionality.
141
142 Description:
143
144 Synopsis: (module_name) Install -> handle
145 */
LoadModuleFunction(vecDynModules & dyn_modules)146 DynamicLoaderModule::LoadModuleFunction::LoadModuleFunction( vecDynModules& dyn_modules )
147 : dyn_modules_( dyn_modules )
148 {
149 }
150
151 void
execute(SLIInterpreter * i) const152 DynamicLoaderModule::LoadModuleFunction::execute( SLIInterpreter* i ) const
153 {
154 i->assert_stack_load( 1 );
155
156 if ( kernel().model_manager.has_user_models() or kernel().model_manager.has_user_prototypes() )
157 {
158 throw DynamicModuleManagementError( "Modules cannot be installed after CopyModel has been called" );
159 }
160
161 sDynModule new_module;
162
163 new_module.name = getValue< std::string >( i->OStack.top() );
164 if ( new_module.name.empty() )
165 {
166 throw DynamicModuleManagementError( "Module name must not be empty." );
167 }
168
169 // check if module already loaded
170 // this check can happen here, since we are comparing dynamically loaded
171 // modules based on the name given to the Install command
172 if ( std::find( dyn_modules_.begin(), dyn_modules_.end(), new_module ) != dyn_modules_.end() )
173 {
174 throw DynamicModuleManagementError( "Module '" + new_module.name + "' is loaded already." );
175 }
176
177 // call lt_dlerror() to reset any error messages hanging around
178 lt_dlerror();
179 // try to open the module
180 const lt_dlhandle hModule = lt_dlopenext( new_module.name.c_str() );
181
182 if ( not hModule )
183 {
184 char* errstr = ( char* ) lt_dlerror();
185 std::string msg = "Module '" + new_module.name + "' could not be opened.";
186 if ( errstr )
187 {
188 msg += "\nThe dynamic loader returned the following error: '" + std::string( errstr ) + "'.";
189 }
190 msg += "\n\nPlease check LD_LIBRARY_PATH (OSX: DYLD_LIBRARY_PATH)!";
191 throw DynamicModuleManagementError( msg );
192 }
193
194 // see if we can find the mod symbol in the module
195 SLIModule* pModule = ( SLIModule* ) lt_dlsym( hModule, "mod" );
196 char* errstr = ( char* ) lt_dlerror();
197 if ( errstr )
198 {
199 lt_dlclose( hModule ); // close module again
200 lt_dlerror(); // remove any error caused by lt_dlclose()
201 throw DynamicModuleManagementError(
202 "Module '" + new_module.name + "' could not be loaded.\n"
203 "The dynamic loader returned the following error: '"
204 + std::string(errstr) + "'.");
205 }
206
207 // check if module is linked in. This test is based on the module name
208 // returned by DynModule::name(), since we have no file names for linked
209 // modules. We can only perform it after we have loaded the module.
210 if ( std::find_if( DynamicLoaderModule::getLinkedModules().begin(),
211 DynamicLoaderModule::getLinkedModules().end(),
212 std::bind( has_name, std::placeholders::_1, pModule->name() ) )
213 != DynamicLoaderModule::getLinkedModules().end() )
214 {
215 lt_dlclose( hModule ); // close module again
216 lt_dlerror(); // remove any error caused by lt_dlclose()
217 throw DynamicModuleManagementError(
218 "Module '" + new_module.name + "' is linked into NEST.\n"
219 "You neither need nor may load it dynamically in addition.");
220 }
221
222 // all is well an we can register the module with the interpreter
223 try
224 {
225 pModule->install( std::cerr, i );
226 }
227 catch ( std::exception& e )
228 {
229 // We should uninstall the partially installed module here, but
230 // this must wait for #152.
231 // For now, we just close the module file and rethrow the exception.
232
233 lt_dlclose( hModule );
234 lt_dlerror(); // remove any error caused by lt_dlclose()
235 throw; // no arg re-throws entire exception, see Stroustrup 14.3.1
236 }
237
238 // add the handle to list of loaded modules
239 new_module.handle = hModule;
240 new_module.pModule = pModule;
241 dyn_modules_.push_back( new_module );
242
243 LOG( M_INFO, "Install", ( "loaded module " + pModule->name() ).c_str() );
244
245 // remove operand and operator from stack
246 i->OStack.pop();
247 i->EStack.pop();
248
249 // put handle to module onto stack
250 int moduleid = dyn_modules_.size() - 1;
251 i->OStack.push( moduleid );
252 ( *moduledict_ )[ new_module.name ] = moduleid;
253
254 // now we can run the module initializer, after we have cleared the EStack
255 if ( not pModule->commandstring().empty() )
256 {
257 Token t = new StringDatum( pModule->commandstring() );
258 i->OStack.push_move( t );
259 Token c = new NameDatum( "initialize_module" );
260 i->EStack.push_move( c );
261 }
262 }
263
264 void
init(SLIInterpreter * i)265 DynamicLoaderModule::init( SLIInterpreter* i )
266 {
267 // bind functions to terminal names
268 i->createcommand( "Install", &loadmodule_function );
269
270 // the ld_* functions return 0 on success and an int > 0 on failure
271 if ( lt_dlinit() )
272 {
273 LOG( M_ERROR, "DynamicLoaderModule::init", "Could not initialize libltdl. No dynamic modules will be available." );
274 }
275
276 if ( lt_dladdsearchdir( NEST_INSTALL_PREFIX "/" NEST_INSTALL_LIBDIR ) )
277 {
278 LOG( M_ERROR, "DynamicLoaderModule::init", "Could not add dynamic module search directory." );
279 }
280 }
281
282
283 int
registerLinkedModule(SLIModule * pModule)284 DynamicLoaderModule::registerLinkedModule( SLIModule* pModule )
285 {
286 assert( pModule != 0 );
287 getLinkedModules().push_back( pModule );
288 return getLinkedModules().size();
289 }
290
291 void
initLinkedModules(SLIInterpreter & interpreter)292 DynamicLoaderModule::initLinkedModules( SLIInterpreter& interpreter )
293 {
294
295 for ( vecLinkedModules::iterator it = getLinkedModules().begin(); it != getLinkedModules().end(); ++it )
296 {
297 interpreter.message( SLIInterpreter::M_STATUS, "DynamicLoaderModule::initLinkedModules", "adding linked module" );
298 interpreter.message( SLIInterpreter::M_STATUS, "DynamicLoaderModule::initLinkedModules", ( *it )->name().c_str() );
299 interpreter.addlinkedusermodule( *it );
300 }
301 }
302
303
304 } // namespace nest
305
306 #endif // HAVE_LIBLTDL
307