00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
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
00049 }
00050 #endif
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
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
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
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
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
00170
00171
00172
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
00188
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
00211
00212
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
00221 return (__meta_plugins.find(plugin_name) != __meta_plugins.end());
00222 }
00223 }
00224
00225
00226
00227
00228
00229
00230
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
00253
00254
00255
00256
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
00275 __meta_plugins.lock();
00276
00277
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
00294
00295 try_real_plugin = true;
00296 }
00297 }
00298
00299 if (try_real_plugin && (plugins.find(*i) == plugins.end()) ) {
00300 try {
00301
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
00320
00321 throw;
00322 }
00323 }
00324 }
00325 }
00326 }
00327
00328
00329
00330
00331
00332
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
00346
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
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
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
00527
00528
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
00541
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 }