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