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/exception.h>
00032 #include <utils/logging/liblogger.h>
00033 #ifdef HAVE_INOTIFY
00034 # include <utils/system/fam_thread.h>
00035 #endif
00036 #include <config/config.h>
00037
00038 #include <algorithm>
00039 #include <cstring>
00040 #include <cstdlib>
00041 #include <cerrno>
00042
00043 #include <sys/types.h>
00044 #include <dirent.h>
00045
00046 namespace fawkes {
00047 #if 0
00048 }
00049 #endif
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065 PluginManager::PluginManager(ThreadCollector *thread_collector,
00066 Configuration *config,
00067 const char *meta_plugin_prefix)
00068 : ConfigurationChangeHandler(meta_plugin_prefix)
00069 {
00070 plugins.clear();
00071 this->thread_collector = thread_collector;
00072 plugin_loader = new PluginLoader(PLUGINDIR, config);
00073 next_plugin_id = 1;
00074 __config = config;
00075 __meta_plugin_prefix = meta_plugin_prefix;
00076
00077 init_pinfo_cache();
00078
00079 __config->add_change_handler(this);
00080
00081 #ifdef HAVE_INOTIFY
00082 __fam_thread = new FamThread();
00083 RefPtr<FileAlterationMonitor> fam = __fam_thread->get_fam();
00084 fam->add_filter("^[^.].*\\.so$");
00085 fam->add_listener(this);
00086 fam->watch_dir(PLUGINDIR);
00087 __fam_thread->start();
00088 #else
00089 LibLogger::log_warn("PluginManager", "File alteration monitoring not available, "
00090 "cannot detect changed plugins on disk.");
00091 #endif
00092 }
00093
00094
00095
00096 PluginManager::~PluginManager()
00097 {
00098 #ifdef HAVE_INOTIFY
00099 __fam_thread->cancel();
00100 __fam_thread->join();
00101 delete __fam_thread;
00102 #endif
00103 __config->rem_change_handler(this);
00104 __pinfo_cache.lock();
00105 __pinfo_cache.clear();
00106 __pinfo_cache.unlock();
00107
00108 for (rpit = plugins.rbegin(); rpit != plugins.rend(); ++rpit) {
00109 thread_collector->force_remove((*rpit).second->threads());
00110 plugin_loader->unload( (*rpit).second );
00111 }
00112 plugins.clear();
00113 plugin_ids.clear();
00114 delete plugin_loader;
00115 }
00116
00117
00118 void
00119 PluginManager::init_pinfo_cache()
00120 {
00121 __pinfo_cache.lock();
00122
00123 DIR *plugin_dir;
00124 struct dirent* dirp;
00125
00126 const char *file_ext = ".so";
00127
00128 if ( NULL == (plugin_dir = opendir(PLUGINDIR)) ) {
00129 throw Exception(errno, "Plugin directory %s could not be opened", plugin_dir);
00130 }
00131
00132 for (unsigned int i = 0; NULL != (dirp = readdir(plugin_dir)); ++i) {
00133 char *file_name = dirp->d_name;
00134 char *pos = strstr(file_name, file_ext);
00135 std::string plugin_name = std::string(file_name).substr(0, strlen(file_name) - strlen(file_ext));
00136 if (NULL != pos) {
00137 try {
00138 __pinfo_cache.push_back(make_pair(plugin_name,
00139 plugin_loader->get_description(plugin_name.c_str())));
00140 } catch (Exception &e) {
00141 LibLogger::log_warn("PluginManager", "Could not get description of plugin %s, "
00142 "exception follows", plugin_name.c_str());
00143 LibLogger::log_warn("PluginManager", e);
00144 }
00145 }
00146 }
00147
00148 closedir(plugin_dir);
00149
00150 try {
00151 Configuration::ValueIterator *i = __config->search(__meta_plugin_prefix.c_str());
00152 while (i->next()) {
00153 if (i->is_string()) {
00154 std::string p = std::string(i->path()).substr(__meta_plugin_prefix.length());
00155 std::string s = std::string("Meta: ") + i->get_string();
00156
00157 __pinfo_cache.push_back(make_pair(p, s));
00158 }
00159 }
00160 delete i;
00161 } catch (Exception &e) {
00162 }
00163
00164 __pinfo_cache.sort();
00165 __pinfo_cache.unlock();
00166 }
00167
00168
00169
00170
00171
00172
00173 std::list<std::pair<std::string, std::string> >
00174 PluginManager::get_available_plugins()
00175 {
00176 std::list<std::pair<std::string, std::string> > rv;
00177
00178 std::list<std::pair<std::string, std::string> >::iterator i;
00179 for (i = __pinfo_cache.begin(); i != __pinfo_cache.end(); ++i) {
00180 rv.push_back(*i);
00181 }
00182
00183 return rv;
00184 }
00185
00186
00187
00188
00189 std::list<std::string>
00190 PluginManager::get_loaded_plugins()
00191 {
00192 std::list<std::string> rv;
00193
00194 plugins.lock();
00195 for (pit = plugins.begin(); pit != plugins.end(); ++pit) {
00196 rv.push_back(pit->first);
00197 }
00198 for (__mpit = __meta_plugins.begin(); __mpit != __meta_plugins.end(); ++__mpit) {
00199 rv.push_back(__mpit->first);
00200 }
00201 plugins.unlock();
00202
00203 return rv;
00204 }
00205
00206
00207
00208
00209
00210
00211 bool
00212 PluginManager::is_loaded(const char *plugin_name)
00213 {
00214 if (plugin_loader->is_loaded(plugin_name)) {
00215 return true;
00216 } else {
00217
00218 return (__meta_plugins.find(plugin_name) != __meta_plugins.end());
00219 }
00220 }
00221
00222
00223
00224
00225
00226
00227
00228
00229 std::list<std::string>
00230 PluginManager::parse_plugin_list(const char *plugin_list)
00231 {
00232 std::list<std::string> rv;
00233
00234 char *plugins = strdup(plugin_list);
00235 char *saveptr;
00236 char *plugin;
00237
00238 plugin = strtok_r(plugins, ",", &saveptr);
00239 while ( plugin ) {
00240 rv.push_back(plugin);
00241 plugin = strtok_r(NULL, ",", &saveptr);
00242 }
00243 free(plugins);
00244
00245 return rv;
00246 }
00247
00248
00249
00250
00251
00252
00253
00254
00255 void
00256 PluginManager::load(const char *plugin_list)
00257 {
00258 std::list<std::string> pp = parse_plugin_list(plugin_list);
00259
00260 for (std::list<std::string>::iterator i = pp.begin(); i != pp.end(); ++i) {
00261 if ( i->length() == 0 ) continue;
00262
00263 bool try_real_plugin = true;
00264 if ( __meta_plugins.find(*i) == __meta_plugins.end() ) {
00265 std::string meta_plugin = __meta_plugin_prefix + *i;
00266 try {
00267 std::string pset = __config->get_string(meta_plugin.c_str());
00268 if (pset.length() == 0) {
00269 throw Exception("Refusing to load an empty meta plugin");
00270 }
00271
00272 __meta_plugins.lock();
00273
00274
00275 __meta_plugins[*i] = pset;
00276 try {
00277 LibLogger::log_info("PluginManager", "Loading plugins %s for meta plugin %s",
00278 pset.c_str(), i->c_str());
00279 load(pset.c_str());
00280 notify_loaded(i->c_str());
00281 } catch (Exception &e) {
00282 e.append("Could not initialize meta plugin %s, aborting loading.", i->c_str());
00283 __meta_plugins.erase(*i);
00284 __meta_plugins.unlock();
00285 throw;
00286 }
00287 __meta_plugins.unlock();
00288
00289 try_real_plugin = false;
00290 } catch (ConfigEntryNotFoundException &e) {
00291
00292
00293 try_real_plugin = true;
00294 }
00295 }
00296
00297 if (try_real_plugin && (plugins.find(*i) == plugins.end()) ) {
00298 try {
00299
00300 Plugin *plugin = plugin_loader->load(i->c_str());
00301 plugins.lock();
00302 try {
00303 thread_collector->add(plugin->threads());
00304 plugins[*i] = plugin;
00305 plugin_ids[*i] = next_plugin_id++;
00306 notify_loaded(i->c_str());
00307 } catch (CannotInitializeThreadException &e) {
00308 e.prepend("Plugin >>> %s <<< could not be initialized, unloading", i->c_str());
00309 plugins.unlock();
00310 plugin_loader->unload(plugin);
00311 throw;
00312 }
00313 plugins.unlock();
00314 } catch (Exception &e) {
00315 if ( __meta_plugins.find(*i) == __meta_plugins.end() ) {
00316
00317 throw;
00318 }
00319 }
00320 }
00321 }
00322 }
00323
00324
00325
00326
00327
00328
00329
00330 void
00331 PluginManager::unload(const char *plugin_name)
00332 {
00333 if ( plugins.find(plugin_name) != plugins.end() ) {
00334 plugins.lock();
00335 try {
00336 thread_collector->remove(plugins[plugin_name]->threads());
00337 plugin_loader->unload(plugins[plugin_name]);
00338 plugins.erase(plugin_name);
00339 plugin_ids.erase(plugin_name);
00340 notify_unloaded(plugin_name);
00341
00342
00343 __meta_plugins.lock();
00344 __mpit = __meta_plugins.begin();
00345 while (__mpit != __meta_plugins.end()) {
00346 std::list<std::string> pp = parse_plugin_list(__mpit->second.c_str());
00347
00348 bool erase = false;
00349 for (std::list<std::string>::iterator i = pp.begin(); i != pp.end(); ++i) {
00350 if ( *i == plugin_name ) {
00351 erase = true;
00352 break;
00353 }
00354 }
00355 if ( erase ) {
00356 LockMap< std::string, std::string >::iterator tmp = __mpit;
00357 ++__mpit;
00358 notify_unloaded(tmp->first.c_str());
00359 __meta_plugins.erase(tmp);
00360 } else {
00361 ++__mpit;
00362 }
00363 }
00364 __meta_plugins.unlock();
00365
00366 } catch (Exception &e) {
00367 LibLogger::log_error("PluginManager", "Could not finalize one or more threads of plugin %s, NOT unloading plugin", plugin_name);
00368 plugins.unlock();
00369 throw;
00370 }
00371 plugins.unlock();
00372 } else if (__meta_plugins.find(plugin_name) != __meta_plugins.end()) {
00373 std::list<std::string> pp = parse_plugin_list(__meta_plugins[plugin_name].c_str());
00374
00375 for (std::list<std::string>::reverse_iterator i = pp.rbegin(); i != pp.rend(); ++i) {
00376 if ( i->length() == 0 ) continue;
00377 if ( (plugins.find(*i) == plugins.end()) &&
00378 (__meta_plugins.find(*i) != __meta_plugins.end()) ) {
00379 continue;
00380 }
00381 __meta_plugins.lock();
00382 __meta_plugins.erase(*i);
00383 __meta_plugins.unlock();
00384 LibLogger::log_info("PluginManager", "UNloading plugin %s for meta plugin %s",
00385 i->c_str(), plugin_name);
00386 unload(i->c_str());
00387 }
00388 }
00389 }
00390
00391
00392 void
00393 PluginManager::config_tag_changed(const char *new_tag)
00394 {
00395 }
00396
00397 void
00398 PluginManager::config_value_changed(const char *path, bool is_default, int value)
00399 {
00400 LibLogger::log_warn("PluginManager", "Integer value changed in meta plugins "
00401 "path prefix at %s, ignoring", path);
00402 }
00403
00404 void
00405 PluginManager::config_value_changed(const char *path, bool is_default, unsigned int value)
00406 {
00407 LibLogger::log_warn("PluginManager", "Unsigned integer value changed in meta "
00408 "plugins path prefix at %s, ignoring", path);
00409 }
00410
00411 void
00412 PluginManager::config_value_changed(const char *path, bool is_default, float value)
00413 {
00414 LibLogger::log_warn("PluginManager", "Float value changed in meta "
00415 "plugins path prefix at %s, ignoring", path);
00416 }
00417
00418 void
00419 PluginManager::config_value_changed(const char *path, bool is_default, bool value)
00420 {
00421 LibLogger::log_warn("PluginManager", "Boolean value changed in meta "
00422 "plugins path prefix at %s, ignoring", path);
00423 }
00424
00425 void
00426 PluginManager::config_comment_changed(const char *path, bool is_default, const char *comment)
00427 {
00428
00429 }
00430
00431 void
00432 PluginManager::config_value_changed(const char *path, bool is_default, const char *value)
00433 {
00434 __pinfo_cache.lock();
00435 std::string p = std::string(path).substr(__meta_plugin_prefix.length());
00436 std::string s = std::string("Meta: ") + value;
00437 std::list<std::pair<std::string, std::string> >::iterator i;
00438 bool found = false;
00439 for (i = __pinfo_cache.begin(); i != __pinfo_cache.end(); ++i) {
00440 if (p == i->first) {
00441 i->second = s;
00442 found = true;
00443 break;
00444 }
00445 }
00446 if (! found) {
00447 __pinfo_cache.push_back(make_pair(p, s));
00448 }
00449 __pinfo_cache.unlock();
00450 }
00451
00452 void
00453 PluginManager::config_value_erased(const char *path, bool is_default)
00454 {
00455 __pinfo_cache.lock();
00456 std::string p = std::string(path).substr(__meta_plugin_prefix.length());
00457 std::list<std::pair<std::string, std::string> >::iterator i;
00458 for (i = __pinfo_cache.begin(); i != __pinfo_cache.end(); ++i) {
00459 if (p == i->first) {
00460 __pinfo_cache.erase(i);
00461 break;
00462 }
00463 }
00464 __pinfo_cache.unlock();
00465 }
00466
00467
00468 void
00469 PluginManager::fam_event(const char *filename, unsigned int mask)
00470 {
00471
00472 const char *file_ext = ".so";
00473
00474 const char *pos = strstr(filename, file_ext);
00475 std::string p = std::string(filename).substr(0, strlen(filename) - strlen(file_ext));
00476 if (NULL != pos) {
00477 __pinfo_cache.lock();
00478 bool found = false;
00479 std::list<std::pair<std::string, std::string> >::iterator i;
00480 for (i = __pinfo_cache.begin(); i != __pinfo_cache.end(); ++i) {
00481 if (p == i->first) {
00482 found = true;
00483 if ((mask & FAM_DELETE) || (mask & FAM_MOVED_FROM)) {
00484 __pinfo_cache.erase(i);
00485 } else {
00486 try {
00487 i->second = plugin_loader->get_description(p.c_str());
00488 } catch (Exception &e) {
00489 LibLogger::log_warn("PluginManager", "Could not get possibly modified "
00490 "description of plugin %s, exception follows",
00491 p.c_str());
00492 LibLogger::log_warn("PluginManager", e);
00493 }
00494 }
00495 break;
00496 }
00497 }
00498 if (! found &&
00499 !(mask & FAM_ISDIR) &&
00500 ((mask & FAM_MODIFY) || (mask & FAM_MOVED_TO) || (mask & FAM_CREATE))) {
00501 if (plugin_loader->is_loaded(p.c_str())) {
00502 LibLogger::log_info("PluginManager", "Plugin %s changed on disk, but is "
00503 "loaded, no new info can be loaded, keeping old.",
00504 p.c_str());
00505 }
00506 try {
00507 std::string s = plugin_loader->get_description(p.c_str());
00508 __pinfo_cache.push_back(make_pair(p, s));
00509 } catch (Exception &e) {
00510 LibLogger::log_warn("PluginManager", "Could not get possibly modified "
00511 "description of plugin %s, exception follows",
00512 p.c_str());
00513 LibLogger::log_warn("PluginManager", e);
00514 }
00515 }
00516
00517 __pinfo_cache.sort();
00518 __pinfo_cache.unlock();
00519 }
00520 }
00521
00522
00523
00524
00525
00526
00527 void
00528 PluginManager::add_listener(PluginManagerListener *listener)
00529 {
00530 __listeners.lock();
00531 __listeners.push_back(listener);
00532 __listeners.sort();
00533 __listeners.unique();
00534 __listeners.unlock();
00535 }
00536
00537
00538
00539
00540 void
00541 PluginManager::remove_listener(PluginManagerListener *listener)
00542 {
00543 __listeners.remove_locked(listener);
00544 }
00545
00546 void
00547 PluginManager::notify_loaded(const char *plugin_name)
00548 {
00549 __listeners.lock();
00550 for (__lit = __listeners.begin(); __lit != __listeners.end(); ++__lit) {
00551 try {
00552 (*__lit)->plugin_loaded(plugin_name);
00553 } catch (Exception &e) {
00554 LibLogger::log_warn("PluginManager", "PluginManagerListener threw exception "
00555 "during notification of plugin loaded, exception follows.");
00556 LibLogger::log_warn("PluginManager", e);
00557 }
00558 }
00559 __listeners.unlock();
00560 }
00561
00562 void
00563 PluginManager::notify_unloaded(const char *plugin_name)
00564 {
00565 __listeners.lock();
00566 for (__lit = __listeners.begin(); __lit != __listeners.end(); ++__lit) {
00567 try {
00568 (*__lit)->plugin_unloaded(plugin_name);
00569 } catch (Exception &e) {
00570 LibLogger::log_warn("PluginManager", "PluginManagerListener threw exception "
00571 "during notification of plugin unloaded, exception follows.");
00572 LibLogger::log_warn("PluginManager", e);
00573 }
00574 }
00575 __listeners.unlock();
00576 }
00577
00578 }