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