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