Fawkes API
Fawkes Development Version
|
00001 00002 /*************************************************************************** 00003 * manager.cpp - Fawkes plugin manager 00004 * 00005 * Created: Wed Nov 15 23:31:55 2006 (on train to Cologne) 00006 * Copyright 2006-2009 Tim Niemueller [www.niemueller.de] 00007 * 00008 ****************************************************************************/ 00009 00010 /* This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; either version 2 of the License, or 00013 * (at your option) any later version. A runtime exception applies to 00014 * this software (see LICENSE.GPL_WRE file mentioned below for details). 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU Library General Public License for more details. 00020 * 00021 * Read the full text in the LICENSE.GPL_WRE file in the doc directory. 00022 */ 00023 00024 #include <plugin/manager.h> 00025 #include <plugin/listener.h> 00026 #include <plugin/loader.h> 00027 00028 #include <core/plugin.h> 00029 #include <core/threading/thread_collector.h> 00030 #include <core/threading/thread_initializer.h> 00031 #include <core/threading/mutex_locker.h> 00032 #include <core/exception.h> 00033 #include <utils/logging/liblogger.h> 00034 #ifdef HAVE_INOTIFY 00035 # include <utils/system/fam_thread.h> 00036 #endif 00037 #include <config/config.h> 00038 00039 #include <algorithm> 00040 #include <cstring> 00041 #include <cstdlib> 00042 #include <cerrno> 00043 00044 #include <sys/types.h> 00045 #include <dirent.h> 00046 00047 namespace fawkes { 00048 #if 0 /* just to make Emacs auto-indent happy */ 00049 } 00050 #endif 00051 00052 /** @class PluginManager <plugin/manager.h> 00053 * Fawkes Plugin Manager. 00054 * This class provides a manager for the plugins used in fawkes. It can 00055 * load and unload modules. 00056 * 00057 * @author Tim Niemueller 00058 */ 00059 00060 /** Constructor. 00061 * @param thread_collector thread manager plugin threads will be added to 00062 * and removed from appropriately. 00063 * @param config Fawkes configuration 00064 * @param meta_plugin_prefix Path prefix for meta plugins 00065 */ 00066 PluginManager::PluginManager(ThreadCollector *thread_collector, 00067 Configuration *config, 00068 const char *meta_plugin_prefix) 00069 : ConfigurationChangeHandler(meta_plugin_prefix) 00070 { 00071 plugins.clear(); 00072 this->thread_collector = thread_collector; 00073 plugin_loader = new PluginLoader(PLUGINDIR, config); 00074 next_plugin_id = 1; 00075 __config = config; 00076 __meta_plugin_prefix = meta_plugin_prefix; 00077 00078 init_pinfo_cache(); 00079 00080 __config->add_change_handler(this); 00081 00082 #ifdef HAVE_INOTIFY 00083 __fam_thread = new FamThread(); 00084 RefPtr<FileAlterationMonitor> fam = __fam_thread->get_fam(); 00085 fam->add_filter("^[^.].*\\.so$"); 00086 fam->add_listener(this); 00087 fam->watch_dir(PLUGINDIR); 00088 __fam_thread->start(); 00089 #else 00090 LibLogger::log_warn("PluginManager", "File alteration monitoring not available, " 00091 "cannot detect changed plugins on disk."); 00092 #endif 00093 } 00094 00095 00096 /** Destructor. */ 00097 PluginManager::~PluginManager() 00098 { 00099 #ifdef HAVE_INOTIFY 00100 __fam_thread->cancel(); 00101 __fam_thread->join(); 00102 delete __fam_thread; 00103 #endif 00104 __config->rem_change_handler(this); 00105 __pinfo_cache.lock(); 00106 __pinfo_cache.clear(); 00107 __pinfo_cache.unlock(); 00108 // Unload all plugins 00109 for (rpit = plugins.rbegin(); rpit != plugins.rend(); ++rpit) { 00110 thread_collector->force_remove((*rpit).second->threads()); 00111 plugin_loader->unload( (*rpit).second ); 00112 } 00113 plugins.clear(); 00114 plugin_ids.clear(); 00115 delete plugin_loader; 00116 } 00117 00118 00119 void 00120 PluginManager::init_pinfo_cache() 00121 { 00122 __pinfo_cache.lock(); 00123 00124 DIR *plugin_dir; 00125 struct dirent* dirp; 00126 /* constant for this somewhere? */ 00127 const char *file_ext = ".so"; 00128 00129 if ( NULL == (plugin_dir = opendir(PLUGINDIR)) ) { 00130 throw Exception(errno, "Plugin directory %s could not be opened", PLUGINDIR); 00131 } 00132 00133 for (unsigned int i = 0; NULL != (dirp = readdir(plugin_dir)); ++i) { 00134 char *file_name = dirp->d_name; 00135 char *pos = strstr(file_name, file_ext); 00136 std::string plugin_name = std::string(file_name).substr(0, strlen(file_name) - strlen(file_ext)); 00137 if (NULL != pos) { 00138 try { 00139 __pinfo_cache.push_back(make_pair(plugin_name, 00140 plugin_loader->get_description(plugin_name.c_str()))); 00141 } catch (Exception &e) { 00142 LibLogger::log_warn("PluginManager", "Could not get description of plugin %s, " 00143 "exception follows", plugin_name.c_str()); 00144 LibLogger::log_warn("PluginManager", e); 00145 } 00146 } 00147 } 00148 00149 closedir(plugin_dir); 00150 00151 try { 00152 Configuration::ValueIterator *i = __config->search(__meta_plugin_prefix.c_str()); 00153 while (i->next()) { 00154 if (i->is_string()) { 00155 std::string p = std::string(i->path()).substr(__meta_plugin_prefix.length()); 00156 std::string s = std::string("Meta: ") + i->get_string(); 00157 00158 __pinfo_cache.push_back(make_pair(p, s)); 00159 } 00160 } 00161 delete i; 00162 } catch (Exception &e) { 00163 } 00164 00165 __pinfo_cache.sort(); 00166 __pinfo_cache.unlock(); 00167 } 00168 00169 /** Generate list of all available plugins. 00170 * @return list of plugins that are available, each plugin is represented by 00171 * a pair of strings. The first string is the plugin name, the second is its 00172 * description. 00173 */ 00174 std::list<std::pair<std::string, std::string> > 00175 PluginManager::get_available_plugins() 00176 { 00177 std::list<std::pair<std::string, std::string> > rv; 00178 00179 std::list<std::pair<std::string, std::string> >::iterator i; 00180 for (i = __pinfo_cache.begin(); i != __pinfo_cache.end(); ++i) { 00181 rv.push_back(*i); 00182 } 00183 00184 return rv; 00185 } 00186 00187 /** Get list of loaded plugins. 00188 * @return list of names of real and meta plugins currently loaded 00189 */ 00190 std::list<std::string> 00191 PluginManager::get_loaded_plugins() 00192 { 00193 std::list<std::string> rv; 00194 00195 plugins.lock(); 00196 for (pit = plugins.begin(); pit != plugins.end(); ++pit) { 00197 rv.push_back(pit->first); 00198 } 00199 plugins.unlock(); 00200 __meta_plugins.lock(); 00201 for (__mpit = __meta_plugins.begin(); __mpit != __meta_plugins.end(); ++__mpit) { 00202 rv.push_back(__mpit->first); 00203 } 00204 __meta_plugins.unlock(); 00205 00206 return rv; 00207 } 00208 00209 00210 /** Check if plugin is loaded. 00211 * @param plugin_name plugin to check if it is loaded 00212 * @return true if the plugin is currently loaded, false otherwise 00213 */ 00214 bool 00215 PluginManager::is_loaded(const char *plugin_name) 00216 { 00217 if (plugin_loader->is_loaded(plugin_name)) { 00218 return true; 00219 } else { 00220 // Could still be a meta plugin 00221 return (__meta_plugins.find(plugin_name) != __meta_plugins.end()); 00222 } 00223 } 00224 00225 00226 /** Parse a list of plugin types. 00227 * Takes a comma-separated list of plugins and parses them into the individual 00228 * plugin names. 00229 * @param plugin_type_list string containing a comma-separated list of plugin types 00230 * @return parsed list of plugin types 00231 */ 00232 std::list<std::string> 00233 PluginManager::parse_plugin_list(const char *plugin_list) 00234 { 00235 std::list<std::string> rv; 00236 00237 char *plugins = strdup(plugin_list); 00238 char *saveptr; 00239 char *plugin; 00240 00241 plugin = strtok_r(plugins, ",", &saveptr); 00242 while ( plugin ) { 00243 rv.push_back(plugin); 00244 plugin = strtok_r(NULL, ",", &saveptr); 00245 } 00246 free(plugins); 00247 00248 return rv; 00249 } 00250 00251 00252 /** Load plugin. 00253 * The loading is interrupted if any of the plugins does not load properly. 00254 * The already loaded plugins are *not* unloaded, but kept. 00255 * @param plugin_list string containing a comma-separated list of plugins 00256 * to load. The plugin list can contain meta plugins. 00257 */ 00258 void 00259 PluginManager::load(const char *plugin_list) 00260 { 00261 std::list<std::string> pp = parse_plugin_list(plugin_list); 00262 00263 for (std::list<std::string>::iterator i = pp.begin(); i != pp.end(); ++i) { 00264 if ( i->length() == 0 ) continue; 00265 00266 bool try_real_plugin = true; 00267 if ( __meta_plugins.find(*i) == __meta_plugins.end() ) { 00268 std::string meta_plugin = __meta_plugin_prefix + *i; 00269 try { 00270 std::string pset = __config->get_string(meta_plugin.c_str()); 00271 if (pset.length() == 0) { 00272 throw Exception("Refusing to load an empty meta plugin"); 00273 } 00274 //printf("Going to load meta plugin %s (%s)\n", i->c_str(), pset.c_str()); 00275 __meta_plugins.lock(); 00276 // Setting has to happen here, so that a meta plugin will not cause an 00277 // endless loop if it references itself! 00278 __meta_plugins[*i] = pset; 00279 __meta_plugins.unlock(); 00280 try { 00281 LibLogger::log_info("PluginManager", "Loading plugins %s for meta plugin %s", 00282 pset.c_str(), i->c_str()); 00283 load(pset.c_str()); 00284 notify_loaded(i->c_str()); 00285 } catch (Exception &e) { 00286 e.append("Could not initialize meta plugin %s, aborting loading.", i->c_str()); 00287 __meta_plugins.erase_locked(*i); 00288 throw; 00289 } 00290 00291 try_real_plugin = false; 00292 } catch (ConfigEntryNotFoundException &e) { 00293 // no meta plugin defined by that name 00294 //printf("No meta plugin defined with the name %s\n", i->c_str()); 00295 try_real_plugin = true; 00296 } 00297 } 00298 00299 if (try_real_plugin && (plugins.find(*i) == plugins.end()) ) { 00300 try { 00301 //printf("Going to load real plugin %s\n", i->c_str()); 00302 Plugin *plugin = plugin_loader->load(i->c_str()); 00303 plugins.lock(); 00304 try { 00305 thread_collector->add(plugin->threads()); 00306 plugins[*i] = plugin; 00307 plugin_ids[*i] = next_plugin_id++; 00308 notify_loaded(i->c_str()); 00309 } catch (CannotInitializeThreadException &e) { 00310 e.prepend("Plugin >>> %s <<< could not be initialized, unloading", i->c_str()); 00311 plugins.unlock(); 00312 plugin_loader->unload(plugin); 00313 throw; 00314 } 00315 plugins.unlock(); 00316 } catch (Exception &e) { 00317 MutexLocker lock(__meta_plugins.mutex()); 00318 if ( __meta_plugins.find(*i) == __meta_plugins.end() ) { 00319 // only throw exception if no meta plugin with that name has 00320 // already been loaded 00321 throw; 00322 } 00323 } 00324 } 00325 } 00326 } 00327 00328 00329 /** Unload plugin. 00330 * Note that this method does not allow to pass a list of plugins, but it will 00331 * only accept a single plugin at a time. 00332 * @param plugin_name plugin to unload, can be a meta plugin. 00333 */ 00334 void 00335 PluginManager::unload(const char *plugin_name) 00336 { 00337 if ( plugins.find(plugin_name) != plugins.end() ) { 00338 plugins.lock(); 00339 try { 00340 thread_collector->remove(plugins[plugin_name]->threads()); 00341 plugin_loader->unload(plugins[plugin_name]); 00342 plugins.erase(plugin_name); 00343 plugin_ids.erase(plugin_name); 00344 notify_unloaded(plugin_name); 00345 // find all meta plugins that required this module, this can no longer 00346 // be considered loaded 00347 __meta_plugins.lock(); 00348 __mpit = __meta_plugins.begin(); 00349 while (__mpit != __meta_plugins.end()) { 00350 std::list<std::string> pp = parse_plugin_list(__mpit->second.c_str()); 00351 00352 bool erase = false; 00353 for (std::list<std::string>::iterator i = pp.begin(); i != pp.end(); ++i) { 00354 if ( *i == plugin_name ) { 00355 erase = true; 00356 break; 00357 } 00358 } 00359 if ( erase ) { 00360 LockMap< std::string, std::string >::iterator tmp = __mpit; 00361 ++__mpit; 00362 notify_unloaded(tmp->first.c_str()); 00363 __meta_plugins.erase(tmp); 00364 } else { 00365 ++__mpit; 00366 } 00367 } 00368 __meta_plugins.unlock(); 00369 00370 } catch (Exception &e) { 00371 LibLogger::log_error("PluginManager", "Could not finalize one or more threads of plugin %s, NOT unloading plugin", plugin_name); 00372 plugins.unlock(); 00373 throw; 00374 } 00375 plugins.unlock(); 00376 } else if (__meta_plugins.find(plugin_name) != __meta_plugins.end()) { 00377 std::list<std::string> pp = parse_plugin_list(__meta_plugins[plugin_name].c_str()); 00378 00379 for (std::list<std::string>::reverse_iterator i = pp.rbegin(); i != pp.rend(); ++i) { 00380 if ( i->length() == 0 ) continue; 00381 if ( (plugins.find(*i) == plugins.end()) && 00382 (__meta_plugins.find(*i) != __meta_plugins.end()) ) { 00383 continue; 00384 } 00385 00386 __meta_plugins.erase_locked(*i); 00387 LibLogger::log_info("PluginManager", "UNloading plugin %s for meta plugin %s", 00388 i->c_str(), plugin_name); 00389 unload(i->c_str()); 00390 } 00391 } 00392 } 00393 00394 00395 void 00396 PluginManager::config_tag_changed(const char *new_tag) 00397 { 00398 } 00399 00400 void 00401 PluginManager::config_value_changed(const char *path, bool is_default, int value) 00402 { 00403 LibLogger::log_warn("PluginManager", "Integer value changed in meta plugins " 00404 "path prefix at %s, ignoring", path); 00405 } 00406 00407 void 00408 PluginManager::config_value_changed(const char *path, bool is_default, unsigned int value) 00409 { 00410 LibLogger::log_warn("PluginManager", "Unsigned integer value changed in meta " 00411 "plugins path prefix at %s, ignoring", path); 00412 } 00413 00414 void 00415 PluginManager::config_value_changed(const char *path, bool is_default, float value) 00416 { 00417 LibLogger::log_warn("PluginManager", "Float value changed in meta " 00418 "plugins path prefix at %s, ignoring", path); 00419 } 00420 00421 void 00422 PluginManager::config_value_changed(const char *path, bool is_default, bool value) 00423 { 00424 LibLogger::log_warn("PluginManager", "Boolean value changed in meta " 00425 "plugins path prefix at %s, ignoring", path); 00426 } 00427 00428 void 00429 PluginManager::config_comment_changed(const char *path, bool is_default, const char *comment) 00430 { 00431 // ignored 00432 } 00433 00434 void 00435 PluginManager::config_value_changed(const char *path, bool is_default, const char *value) 00436 { 00437 __pinfo_cache.lock(); 00438 std::string p = std::string(path).substr(__meta_plugin_prefix.length()); 00439 std::string s = std::string("Meta: ") + value; 00440 std::list<std::pair<std::string, std::string> >::iterator i; 00441 bool found = false; 00442 for (i = __pinfo_cache.begin(); i != __pinfo_cache.end(); ++i) { 00443 if (p == i->first) { 00444 i->second = s; 00445 found = true; 00446 break; 00447 } 00448 } 00449 if (! found) { 00450 __pinfo_cache.push_back(make_pair(p, s)); 00451 } 00452 __pinfo_cache.unlock(); 00453 } 00454 00455 void 00456 PluginManager::config_value_erased(const char *path, bool is_default) 00457 { 00458 __pinfo_cache.lock(); 00459 std::string p = std::string(path).substr(__meta_plugin_prefix.length()); 00460 std::list<std::pair<std::string, std::string> >::iterator i; 00461 for (i = __pinfo_cache.begin(); i != __pinfo_cache.end(); ++i) { 00462 if (p == i->first) { 00463 __pinfo_cache.erase(i); 00464 break; 00465 } 00466 } 00467 __pinfo_cache.unlock(); 00468 } 00469 00470 00471 void 00472 PluginManager::fam_event(const char *filename, unsigned int mask) 00473 { 00474 /* constant for this somewhere? */ 00475 const char *file_ext = ".so"; 00476 00477 const char *pos = strstr(filename, file_ext); 00478 std::string p = std::string(filename).substr(0, strlen(filename) - strlen(file_ext)); 00479 if (NULL != pos) { 00480 __pinfo_cache.lock(); 00481 bool found = false; 00482 std::list<std::pair<std::string, std::string> >::iterator i; 00483 for (i = __pinfo_cache.begin(); i != __pinfo_cache.end(); ++i) { 00484 if (p == i->first) { 00485 found = true; 00486 if ((mask & FAM_DELETE) || (mask & FAM_MOVED_FROM)) { 00487 __pinfo_cache.erase(i); 00488 } else { 00489 try { 00490 i->second = plugin_loader->get_description(p.c_str()); 00491 } catch (Exception &e) { 00492 LibLogger::log_warn("PluginManager", "Could not get possibly modified " 00493 "description of plugin %s, exception follows", 00494 p.c_str()); 00495 LibLogger::log_warn("PluginManager", e); 00496 } 00497 } 00498 break; 00499 } 00500 } 00501 if (! found && 00502 !(mask & FAM_ISDIR) && 00503 ((mask & FAM_MODIFY) || (mask & FAM_MOVED_TO) || (mask & FAM_CREATE))) { 00504 if (plugin_loader->is_loaded(p.c_str())) { 00505 LibLogger::log_info("PluginManager", "Plugin %s changed on disk, but is " 00506 "loaded, no new info can be loaded, keeping old.", 00507 p.c_str()); 00508 } 00509 try { 00510 std::string s = plugin_loader->get_description(p.c_str()); 00511 __pinfo_cache.push_back(make_pair(p, s)); 00512 } catch (Exception &e) { 00513 LibLogger::log_warn("PluginManager", "Could not get possibly modified " 00514 "description of plugin %s, exception follows", 00515 p.c_str()); 00516 LibLogger::log_warn("PluginManager", e); 00517 } 00518 } 00519 00520 __pinfo_cache.sort(); 00521 __pinfo_cache.unlock(); 00522 } 00523 } 00524 00525 00526 /** Add listener. 00527 * Listeners are notified of plugin load and unloda events. 00528 * @param listener listener to add 00529 */ 00530 void 00531 PluginManager::add_listener(PluginManagerListener *listener) 00532 { 00533 __listeners.lock(); 00534 __listeners.push_back(listener); 00535 __listeners.sort(); 00536 __listeners.unique(); 00537 __listeners.unlock(); 00538 } 00539 00540 /** Remove listener. 00541 * @param listener listener to remove 00542 */ 00543 void 00544 PluginManager::remove_listener(PluginManagerListener *listener) 00545 { 00546 __listeners.remove_locked(listener); 00547 } 00548 00549 void 00550 PluginManager::notify_loaded(const char *plugin_name) 00551 { 00552 __listeners.lock(); 00553 for (__lit = __listeners.begin(); __lit != __listeners.end(); ++__lit) { 00554 try { 00555 (*__lit)->plugin_loaded(plugin_name); 00556 } catch (Exception &e) { 00557 LibLogger::log_warn("PluginManager", "PluginManagerListener threw exception " 00558 "during notification of plugin loaded, exception follows."); 00559 LibLogger::log_warn("PluginManager", e); 00560 } 00561 } 00562 __listeners.unlock(); 00563 } 00564 00565 void 00566 PluginManager::notify_unloaded(const char *plugin_name) 00567 { 00568 __listeners.lock(); 00569 for (__lit = __listeners.begin(); __lit != __listeners.end(); ++__lit) { 00570 try { 00571 (*__lit)->plugin_unloaded(plugin_name); 00572 } catch (Exception &e) { 00573 LibLogger::log_warn("PluginManager", "PluginManagerListener threw exception " 00574 "during notification of plugin unloaded, exception follows."); 00575 LibLogger::log_warn("PluginManager", e); 00576 } 00577 } 00578 __listeners.unlock(); 00579 } 00580 00581 } // end namespace fawkes