Fawkes API  Fawkes Development Version
module.cpp
1 
2 /***************************************************************************
3  * module.cpp - interface for modules (i.e. shared object, dynamic library)
4  *
5  * Created: Wed May 09 11:03:40 2007
6  * Copyright 2006-2007 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 #include <utils/system/dynamic_module/module.h>
25 #include <utils/system/file.h>
26 
27 #include <cstring>
28 #include <dlfcn.h>
29 
30 namespace fawkes {
31 
32 /** @class ModuleOpenException <utils/system/dynamic_module/module.h>
33  * Opening a module failed.
34  * Thrown if a call to Module::open() failed.
35  */
36 
37 /** Constructor.
38  * @param msg message
39  */
41  : Exception(msg)
42 {
43 }
44 
45 /** @class Module <utils/system/dynamic_module/module.h>
46  * Dynamic module loader for Linux, FreeBSD, and MacOS X.
47  * A Module implementation for the dl dynamic loader library that comes
48  * with glibc, applicable for Linux, FreeBSD, and MacOS X Systems.
49  *
50  * For nice reading and hints about using dynamic module loading with C++ you
51  * should have a look at
52  * http://www.isotton.com/howtos/C++-dlopen-mini-HOWTO/C++-dlopen-mini-HOWTO.html
53  * @author Tim Niemueller
54  */
55 
56 // SOEXT is a macro passed in from the build system and set in config.mk or
57 // a build type specific config file.
58 const char * Module::FILE_EXTENSION = SOEXT;
59 
60 /** Constructor.
61  * @param filename full filename of the module
62  * @param flags module flags
63  */
64 Module::Module(std::string filename, Module::ModuleFlags flags)
65 {
66  __filename = filename;
67  __flags = flags;
68 
69  __handle = NULL;
70 
71  __is_resident = false;
72  __ref_count = 0;
73 }
74 
75 
76 /** Destructor.
77  * Closes the module. */
79 {
80  close();
81 }
82 
83 /** Open the module
84  * @return true if the module could be opened, false otherwise
85  * @exception ModuleOpenException thrown if there was any problem
86  * while loading the module
87  */
88 void
90 {
91  if ( __handle != NULL ) return;
92 
93  // Note: We assume Linux-style shared objects
94  std::string full_filename = "";
95  full_filename = __filename;
96  // . SOEXT
97  if ( full_filename.find("." SOEXT, 0) != (full_filename.length() - 1 - strlen(FILE_EXTENSION)) ) {
98  // filename has no proper ending
99  full_filename += "." SOEXT;
100  }
101 
102  int tflags = 0;
103  tflags |= ((__flags & MODULE_BIND_LAZY) != 0) ? RTLD_LAZY : RTLD_NOW;
104  tflags |= ((__flags & MODULE_BIND_NOW) != 0) ? RTLD_NOW : 0;
105  tflags |= ((__flags & MODULE_BIND_LOCAL) != 0) ? RTLD_LOCAL : 0;
106  tflags |= ((__flags & MODULE_BIND_GLOBAL) != 0) ? RTLD_GLOBAL : 0;
107  tflags |= ((__flags & MODULE_NODELETE) != 0) ? RTLD_NODELETE : 0;
108 #ifdef linux
109  tflags |= ((__flags & MODULE_BIND_DEEP) != 0) ? RTLD_DEEPBIND : 0;
110 #endif
111 
112  if ( full_filename == "") {
113  __handle = dlopen (NULL, tflags);
114 
115  __filename = "main";
116  __is_resident = true;
117  __ref_count = 1;
118  } else {
119 
120  // check whether we have a readable file right away
121  if (File::is_regular(full_filename.c_str())) {
122  // ok, try loading the module
123  __handle = dlopen(full_filename.c_str(), tflags);
124 
125  if ( NULL == __handle) {
126  const char *err = dlerror();
127  if ( NULL == err ) {
128  throw ModuleOpenException("dlopen failed with an unknown error");
129  } else {
130  ModuleOpenException e("dlopen failed");
131  e.append("dlerror: %s", err);
132  throw e;
133  }
134  } else {
135  __is_resident = false;
136  __ref_count = 1;
137  }
138  } else {
139  ModuleOpenException e("Cannot open module");
140  e.append("File '%s' does not exist", full_filename.c_str());
141  throw e;
142  }
143  }
144 }
145 
146 
147 /** Close the module
148  * @return Returns true if the module could be closed, false otherwise
149  */
150 bool
152 {
153  if ( __handle == NULL ) return true;
154 
155  if ( __ref_count > 0 ) --__ref_count;
156 
157  if ( (__ref_count == 0) && ! __is_resident ) {
158  if ( dlclose(__handle) != 0 ) {
159  __handle = NULL;
160  return false;
161  }
162  __handle = NULL;
163  }
164 
165  return true;
166 }
167 
168 
169 /** Increment the reference count of this module */
170 void
172 {
173  ++__ref_count;
174 }
175 
176 
177 /** Decrease the reference count of this module */
178 void
180 {
181  if ( __ref_count > 0 ) {
182  --__ref_count;
183  }
184 }
185 
186 
187 /** Check if there are no reference to this module
188  * @return Returns true if there are no references to this module,
189  * false if there is at least one reference
190  */
191 bool
193 {
194  return (__ref_count == 0);
195 }
196 
197 
198 /** Get the reference count of this module
199  * @return Returns the number of references to this module
200  */
201 unsigned int
203 {
204  return __ref_count;
205 }
206 
207 
208 /** Compare to another Module instance
209  * @param cmod a reference to the other comparison instance
210  * @return Returns true, if the full file names of both modules are the
211  * same, false otherwise
212  */
213 bool
215 {
216  return (__filename == cmod.__filename);
217 }
218 
219 
220 /** Check if the module has the given symbol
221  * @param symbol_name The name of the symbol.
222  * NOTE: C++ symbols are mangled with type info and thus are not plainly
223  * available as symbol name. Use extern "C" to avoid this.
224  * Read
225  * http://www.isotton.com/howtos/C++-dlopen-mini-HOWTO/C++-dlopen-mini-HOWTO.html
226  * for more information on this topic.
227  * @return Returns true if the symbol was found, false otherwise
228  */
229 bool
230 Module::has_symbol(const char *symbol_name)
231 {
232  if( symbol_name == NULL ) {
233  return false;
234  }
235  if ( __handle == NULL ) {
236  return false;
237  }
238 
239  return ( dlsym( __handle, symbol_name ) != NULL );
240 }
241 
242 
243 /** Get a symbol from the module
244  * @param symbol_name The name of the symbol.
245  * NOTE: C++ symbols are mangled with type info and thus are not plainly
246  * available as symbol name. Use extern "C" to avoid this.
247  * Read
248  * http://www.isotton.com/howtos/C++-dlopen-mini-HOWTO/C++-dlopen-mini-HOWTO.html
249  * for more information on this topic.
250  * @return Returns a pointer to the symbol or NULL if symbol was not found
251  */
252 void *
253 Module::get_symbol(const char *symbol_name)
254 {
255  if( symbol_name == NULL ) return NULL;
256  if ( __handle == NULL ) return NULL;
257 
258  return dlsym( __handle, symbol_name );
259 }
260 
261 
262 /** Get file extension for dl modules
263  * @return Returns the file extension for dl modules, this is "so" on Linux
264  * and FreeBSD systems, and dylib on MacOS X. It is defined at compile time
265  * in config.mk.
266  */
267 const char *
269 {
270  return FILE_EXTENSION;
271 }
272 
273 
274 /** Get the full file name of the module
275  * @return Returns a string with the full file name of the module
276  */
277 std::string
279 {
280  return __filename;
281 }
282 
283 
284 /** Get the base file name of the module
285  * @return Returns the base file name of the module. On Unix systems this is
286  * everything after the last slash
287  */
288 std::string
290 {
291  if ( __filename.find("/", 0) != std::string::npos ) {
292  std::string rv = __filename.substr(__filename.rfind("/", __filename.length()) + 1, __filename.length());
293  return rv;
294  } else {
295  return __filename.c_str();
296  }
297 }
298 
299 } // end namespace fawkes
virtual void unref()
Decrease the reference count of this module.
Definition: module.cpp:179
virtual bool close()
Close the module.
Definition: module.cpp:151
ModuleOpenException(const char *msg)
Constructor.
Definition: module.cpp:40
virtual void open()
Open the module.
Definition: module.cpp:89
Fawkes library namespace.
virtual void ref()
Increment the reference count of this module.
Definition: module.cpp:171
virtual ~Module()
Destructor.
Definition: module.cpp:78
virtual unsigned int get_ref_count()
Get the reference count of this module.
Definition: module.cpp:202
ModuleFlags
Flags for the loading process.
Definition: module.h:44
virtual bool notref()
Check if there are no reference to this module.
Definition: module.cpp:192
Dynamic module loader for Linux, FreeBSD, and MacOS X.
Definition: module.h:40
Base class for exceptions in Fawkes.
Definition: exception.h:36
virtual std::string get_base_filename()
Get the base file name of the module.
Definition: module.cpp:289
virtual void * get_symbol(const char *symbol_name)
Get a symbol from the module.
Definition: module.cpp:253
virtual bool operator==(const Module &cmod)
Compare to another Module instance.
Definition: module.cpp:214
static bool is_regular(const char *filename)
Check if a file is a regular file.
Definition: file.cpp:160
Module(std::string filename, ModuleFlags flags=MODULE_FLAGS_DEFAULT)
Constructor.
Definition: module.cpp:64
static const char * get_file_extension()
Get file extension for dl modules.
Definition: module.cpp:268
void append(const char *format,...)
Append messages to the message list.
Definition: exception.cpp:341
virtual std::string get_filename()
Get the full file name of the module.
Definition: module.cpp:278
virtual bool has_symbol(const char *symbol_name)
Check if the module has the given symbol.
Definition: module.cpp:230
Opening a module failed.
Definition: module.h:34