handler.cpp

00001
00002 /***************************************************************************
00003  *  handler.cpp - Fawkes plugin network handler
00004  *
00005  *  Created: Thu Feb 12 10:36:15 2009
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/net/handler.h>
00026 #include <plugin/net/messages.h>
00027 #include <plugin/net/list_message.h>
00028
00029 #include <utils/logging/liblogger.h>
00030
00031 #include <netcomm/fawkes/component_ids.h>
00032 #include <netcomm/fawkes/hub.h>
00033
00034 #include <algorithm>
00035 #include <cstring>
00036 #include <cstdlib>
00037 #include <cerrno>
00038
00039 namespace fawkes {
00040 #if 0 /* just to make Emacs auto-indent happy */
00041 }
00042 #endif
00043 
00044 /** @class PluginNetworkHandler <plugin/net/handler.h>
00045  * Fawkes Plugin Network Handler.
00046  * This network handler handles requests of plugin lists and for loading/unloading
00047  * plugins received over the network.
00048  *
00049  * @author Tim Niemueller
00050  */
00051
00052 /* IMPORANT IMPLEMENTER'S NOTE
00053  *
00054  * If you are going to work on this code mind the following: it is assumed
00055  * that only loop() will pop messages from the inbound queue. Thus the inbound
00056  * queue is only locked for this pop operation, not for the whole access time.
00057  * This is true as long as messages are only appended from the outside!
00058  * This is necessary to ensure that handle_network_message() will not hang
00059  * waiting for the queue lock.
00060  */
00061 
00062 /** Constructor.
00063  * @param manager plugin manager for the actual work
00064  * @param hub Fawkes network hub
00065  */
00066 PluginNetworkHandler::PluginNetworkHandler(PluginManager *manager, FawkesNetworkHub *hub)
00067   : Thread("PluginNetworkHandler", Thread::OPMODE_WAITFORWAKEUP),
00068     FawkesNetworkHandler(FAWKES_CID_PLUGINMANAGER)
00069 {
00070   __manager = manager;
00071   __hub = hub;
00072
00073   __manager->add_listener(this);
00074   __hub->add_handler(this);
00075 }
00076
00077 
00078 /** Destructor. */
00079 PluginNetworkHandler::~PluginNetworkHandler()
00080 {
00081   __hub->remove_handler(this);
00082   __manager->remove_listener(this);
00083 }
00084
00085 
00086 /** Generate list of all available plugins.
00087  * All files with the extension .so in the PLUGINDIR are returned.
00088  * @param num_plugins pointer to an unsigned int where the number
00089  * of all plugins is stored
00090  * @param plugin_list pointer to the string array where the list of 
00091  * all plugins is stored. Memory is allocated at this address and
00092  * has to be freed by the caller!
00093  */
00094 PluginListMessage *
00095 PluginNetworkHandler::list_avail()
00096 {
00097   PluginListMessage *m = new PluginListMessage();
00098
00099   std::list<std::pair<std::string, std::string> > available_plugins;
00100   available_plugins = __manager->get_available_plugins();
00101
00102   std::list<std::pair<std::string, std::string> >::iterator i;
00103   for (i = available_plugins.begin(); i != available_plugins.end(); ++i) {
00104     m->append(i->first.c_str(), i->first.length());
00105     m->append(i->second.c_str(), i->second.length());
00106   }
00107   return m;
00108 }
00109
00110 PluginListMessage *
00111 PluginNetworkHandler::list_loaded()
00112 {
00113   PluginListMessage *m = new PluginListMessage();
00114
00115   std::list<std::string> loaded_plugins;
00116   loaded_plugins = __manager->get_loaded_plugins();
00117
00118   std::list<std::string>::iterator i;
00119   for (i = loaded_plugins.begin(); i != loaded_plugins.end(); ++i) {
00120     m->append(i->c_str(), i->length());
00121   }
00122
00123   return m;
00124 }
00125
00126
00127 void
00128 PluginNetworkHandler::send_load_failure(const char *plugin_name,
00129                                        unsigned int client_id)
00130 {
00131   try {
00132     plugin_load_failed_msg_t *r = (plugin_load_failed_msg_t *)calloc(1, sizeof(plugin_load_failed_msg_t));
00133     strncpy(r->name, plugin_name, PLUGIN_MSG_NAME_LENGTH);
00134     __hub->send(client_id, FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_LOAD_FAILED,
00135                 r, sizeof(plugin_load_failed_msg_t));
00136   } catch (Exception &e) {
00137     LibLogger::log_warn("PluginNetworkHandler", "Failed to send load failure, exception follows");
00138     LibLogger::log_warn("PluginNetworkHandler", e);
00139   }
00140 }
00141
00142
00143 void
00144 PluginNetworkHandler::send_load_success(const char *plugin_name, unsigned int client_id)
00145 {
00146   try {
00147     plugin_loaded_msg_t *r = (plugin_loaded_msg_t *)calloc(1, sizeof(plugin_loaded_msg_t));
00148     strncpy(r->name, plugin_name, PLUGIN_MSG_NAME_LENGTH);
00149     __hub->send(client_id, FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_LOADED,
00150                 r, sizeof(plugin_loaded_msg_t));
00151   } catch (Exception &e) {
00152     LibLogger::log_warn("PluginNetworkHandler", "Failed to send load success, exception follows");
00153     LibLogger::log_warn("PluginNetworkHandler", e);
00154   }
00155 }
00156
00157
00158 void
00159 PluginNetworkHandler::send_unloaded(const char *plugin_name)
00160 {
00161   __subscribers.lock();
00162   try {
00163     for (__ssit = __subscribers.begin(); __ssit != __subscribers.end(); ++__ssit) {
00164       send_unload_success(plugin_name, *__ssit);
00165     }
00166   } catch (Exception &e) {
00167     LibLogger::log_warn("PluginNetworkHandler", "Failed to send unloaded, exception follows");
00168     LibLogger::log_warn("PluginNetworkHandler", e);
00169   }
00170   __subscribers.unlock();
00171 }
00172
00173
00174 void
00175 PluginNetworkHandler::send_loaded(const char *plugin_name)
00176 {
00177   __subscribers.lock();
00178   try {
00179     for (__ssit = __subscribers.begin(); __ssit != __subscribers.end(); ++__ssit) {
00180       send_load_success(plugin_name, *__ssit);
00181     }
00182   } catch (Exception &e) {
00183     LibLogger::log_warn("PluginNetworkHandler", "Failed to send loaded, exception follows");
00184     LibLogger::log_warn("PluginNetworkHandler", e);
00185   }
00186   __subscribers.unlock();
00187 }
00188
00189
00190 void
00191 PluginNetworkHandler::send_unload_failure(const char *plugin_name,
00192                                          unsigned int client_id)
00193 {
00194   try {
00195     plugin_unload_failed_msg_t *r = (plugin_unload_failed_msg_t *)calloc(1, sizeof(plugin_unload_failed_msg_t));
00196     strncpy(r->name, plugin_name, PLUGIN_MSG_NAME_LENGTH);
00197     __hub->send(client_id, FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_UNLOAD_FAILED,
00198                 r, sizeof(plugin_unload_failed_msg_t));
00199   } catch (Exception &e) {
00200     LibLogger::log_warn("PluginNetworkHandler", "Failed to send unload failure, exception follows");
00201     LibLogger::log_warn("PluginNetworkHandler", e);
00202   }
00203 }
00204
00205
00206 void
00207 PluginNetworkHandler::send_unload_success(const char *plugin_name, unsigned int client_id)
00208 {
00209   try {
00210     plugin_unloaded_msg_t *r = (plugin_unloaded_msg_t *)calloc(1, sizeof(plugin_unloaded_msg_t));
00211     strncpy(r->name, plugin_name, PLUGIN_MSG_NAME_LENGTH);
00212     __hub->send(client_id, FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_UNLOADED,
00213                 r, sizeof(plugin_unloaded_msg_t));
00214   } catch (Exception &e) {
00215     LibLogger::log_warn("PluginNetworkHandler", "Failed to send unload success, exception follows");
00216     LibLogger::log_warn("PluginNetworkHandler", e);
00217   }
00218 }
00219
00220
00221 
00222 /** Load plugin.
00223  * The loading is interrupted if any of the plugins does not load properly.
00224  * The already loaded plugins are *not* unloaded, but kept.
00225  * @param plugin_list string containing a comma-separated list of plugins
00226  * to load. The plugin list can contain meta plugins.
00227  * @param clid Fawkes network client ID of client that gets a success message
00228  * with the exact string that was put into
00229  */
00230 void
00231 PluginNetworkHandler::load(const char *plugin_list, unsigned int clid)
00232 {
00233   try {
00234     __manager->load(plugin_list);
00235     send_load_success(plugin_list, clid);
00236   } catch (Exception &e) {
00237     LibLogger::log_error("PluginNetworkHandler", "Failed to load plugin %s", plugin_list);
00238     LibLogger::log_error("PluginNetworkHandler", e);
00239     send_load_failure(plugin_list, clid);
00240   }
00241 }
00242
00243 
00244 /** Unload plugin.
00245  * Note that this method does not allow to pass a list of plugins, but it will
00246  * only accept a single plugin at a time.
00247  * @param plugin_name plugin to unload, can be a meta plugin.
00248  * @param clid Fawkes network client ID of client that gets a success message
00249  * with the exact string that was put into
00250  */
00251 void
00252 PluginNetworkHandler::unload(const char *plugin_name, unsigned int clid)
00253 {
00254   try {
00255     __manager->unload(plugin_name);
00256     send_unload_success(plugin_name, clid);
00257   } catch (Exception &e) {
00258     LibLogger::log_error("PluginNetworkHandler", "Failed to unload plugin %s", plugin_name);
00259     LibLogger::log_error("PluginNetworkHandler", e);
00260     send_unload_failure(plugin_name, clid);
00261   }
00262 }
00263
00264 
00265 /** Process all network messages that have been received.
00266  */
00267 void
00268 PluginNetworkHandler::loop()
00269 {
00270   while ( ! __inbound_queue.empty() ) {
00271     FawkesNetworkMessage *msg = __inbound_queue.front();
00272
00273     switch (msg->msgid()) {
00274     case MSG_PLUGIN_LOAD:
00275       if ( msg->payload_size() != sizeof(plugin_load_msg_t) ) {
00276         LibLogger::log_error("PluginNetworkHandler", "Invalid load message size");
00277       } else {
00278         plugin_load_msg_t *m = (plugin_load_msg_t *)msg->payload();
00279         char name[PLUGIN_MSG_NAME_LENGTH + 1];
00280         name[PLUGIN_MSG_NAME_LENGTH] = 0;
00281         strncpy(name, m->name, PLUGIN_MSG_NAME_LENGTH);
00282
00283         if ( __manager->is_loaded(name) ) {
00284           LibLogger::log_info("PluginNetworkHandler", "Client requested loading of %s which is already loaded", name);
00285           send_load_success(name, msg->clid());
00286         } else {
00287           LibLogger::log_info("PluginNetworkHandler", "Loading plugin %s", name);
00288           load(name, msg->clid());
00289         }
00290       }
00291       break;
00292
00293     case MSG_PLUGIN_UNLOAD:
00294       if ( msg->payload_size() != sizeof(plugin_unload_msg_t) ) {
00295         LibLogger::log_error("PluginNetworkHandler", "Invalid unload message size.");
00296       } else {
00297         plugin_unload_msg_t *m = (plugin_unload_msg_t *)msg->payload();
00298         char name[PLUGIN_MSG_NAME_LENGTH + 1];
00299         name[PLUGIN_MSG_NAME_LENGTH] = 0;
00300         strncpy(name, m->name, PLUGIN_MSG_NAME_LENGTH);
00301
00302         if ( !__manager->is_loaded(name) ) {
00303           LibLogger::log_info("PluginNetworkHandler", "Client requested unloading of %s which is not loaded", name);
00304           send_unload_success(name, msg->clid());
00305         } else {
00306           LibLogger::log_info("PluginNetworkHandler", "UNloading plugin %s", name);
00307           unload(name, msg->clid());
00308         }
00309       }
00310       break;
00311
00312     case MSG_PLUGIN_LIST_AVAIL:
00313       try {
00314         LibLogger::log_debug("PluginNetworkHandler", "Sending list of all available plugins");
00315         PluginListMessage *plm = list_avail();
00316         __hub->send(msg->clid(), FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_AVAIL_LIST, plm);
00317       } catch (Exception &e) {
00318         __hub->send(msg->clid(), FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_AVAIL_LIST_FAILED);
00319       }
00320       break;
00321
00322     case MSG_PLUGIN_LIST_LOADED:
00323       try {
00324         LibLogger::log_debug("PluginNetworkHandler", "Sending list of all loaded plugins");
00325         PluginListMessage *plm = list_loaded();
00326         __hub->send(msg->clid(), FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_LOADED_LIST, plm);
00327       } catch (Exception &e) {
00328         __hub->send(msg->clid(), FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_LOADED_LIST_FAILED);
00329       }
00330       break;
00331
00332     case MSG_PLUGIN_SUBSCRIBE_WATCH:
00333       __subscribers.lock();
00334       __subscribers.push_back(msg->clid());
00335       __subscribers.sort();
00336       __subscribers.unique();
00337       __subscribers.unlock();
00338       break;
00339
00340     case MSG_PLUGIN_UNSUBSCRIBE_WATCH:
00341       __subscribers.remove_locked(msg->clid());
00342       break;
00343
00344     default:
00345       // error
00346       break;
00347     }
00348
00349     msg->unref();
00350     __inbound_queue.pop_locked();
00351   }
00352 }
00353
00354
00355 void
00356 PluginNetworkHandler::handle_network_message(FawkesNetworkMessage *msg)
00357 {
00358   msg->ref();
00359   __inbound_queue.push_locked(msg);
00360   wakeup();
00361 }
00362
00363
00364 void
00365 PluginNetworkHandler::client_connected(unsigned int clid)
00366 {
00367 }
00368
00369
00370 void
00371 PluginNetworkHandler::client_disconnected(unsigned int clid)
00372 {
00373   __subscribers.remove_locked(clid);
00374 }
00375
00376 void
00377 PluginNetworkHandler::plugin_loaded(const char *plugin_name)
00378 {
00379   send_loaded(plugin_name);
00380 }
00381
00382 void
00383 PluginNetworkHandler::plugin_unloaded(const char *plugin_name)
00384 {
00385   send_unloaded(plugin_name);
00386 }
00387
00388 } // end namespace fawkes