Fawkes API  Fawkes Development Version
handler.cpp
1 
2 /***************************************************************************
3  * handler.cpp - Fawkes plugin network handler
4  *
5  * Created: Thu Feb 12 10:36:15 2009
6  * Copyright 2006-2009 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 #include <plugin/manager.h>
25 #include <plugin/net/handler.h>
26 #include <plugin/net/messages.h>
27 #include <plugin/net/list_message.h>
28 
29 #include <logging/liblogger.h>
30 
31 #include <netcomm/fawkes/component_ids.h>
32 #include <netcomm/fawkes/hub.h>
33 
34 #include <algorithm>
35 #include <cstring>
36 #include <cstdlib>
37 #include <cerrno>
38 
39 namespace fawkes {
40 #if 0 /* just to make Emacs auto-indent happy */
41 }
42 #endif
43 
44 /** @class PluginNetworkHandler <plugin/net/handler.h>
45  * Fawkes Plugin Network Handler.
46  * This network handler handles requests of plugin lists and for loading/unloading
47  * plugins received over the network.
48  *
49  * @author Tim Niemueller
50  */
51 
52 /* IMPORANT IMPLEMENTER'S NOTE
53  *
54  * If you are going to work on this code mind the following: it is assumed
55  * that only loop() will pop messages from the inbound queue. Thus the inbound
56  * queue is only locked for this pop operation, not for the whole access time.
57  * This is true as long as messages are only appended from the outside!
58  * This is necessary to ensure that handle_network_message() will not hang
59  * waiting for the queue lock.
60  */
61 
62 /** Constructor.
63  * @param manager plugin manager for the actual work
64  * @param hub Fawkes network hub
65  */
67  FawkesNetworkHub *hub)
68  : Thread("PluginNetworkHandler", Thread::OPMODE_WAITFORWAKEUP),
69  FawkesNetworkHandler(FAWKES_CID_PLUGINMANAGER)
70 {
71  __manager = manager;
72  __hub = hub;
73 
74  __manager->add_listener(this);
75  __hub->add_handler(this);
76 }
77 
78 
79 /** Destructor. */
81 {
82  __hub->remove_handler(this);
83  __manager->remove_listener(this);
84 }
85 
86 
87 /** Generate list of all available plugins.
88  * All files with the extension .so in the PLUGINDIR are returned.
89  * @param num_plugins pointer to an unsigned int where the number
90  * of all plugins is stored
91  * @param plugin_list pointer to the string array where the list of
92  * all plugins is stored. Memory is allocated at this address and
93  * has to be freed by the caller!
94  */
96 PluginNetworkHandler::list_avail()
97 {
99 
100  std::list<std::pair<std::string, std::string> > available_plugins;
101  available_plugins = __manager->get_available_plugins();
102 
103  std::list<std::pair<std::string, std::string> >::iterator i;
104  for (i = available_plugins.begin(); i != available_plugins.end(); ++i) {
105  m->append(i->first.c_str(), i->first.length());
106  m->append(i->second.c_str(), i->second.length());
107  }
108  return m;
109 }
110 
112 PluginNetworkHandler::list_loaded()
113 {
115 
116  std::list<std::string> loaded_plugins;
117  loaded_plugins = __manager->get_loaded_plugins();
118 
119  std::list<std::string>::iterator i;
120  for (i = loaded_plugins.begin(); i != loaded_plugins.end(); ++i) {
121  m->append(i->c_str(), i->length());
122  }
123 
124  return m;
125 }
126 
127 
128 void
129 PluginNetworkHandler::send_load_failure(const char *plugin_name,
130  unsigned int client_id)
131 {
132  try {
134  strncpy(r->name, plugin_name, PLUGIN_MSG_NAME_LENGTH);
135  __hub->send(client_id, FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_LOAD_FAILED,
136  r, sizeof(plugin_load_failed_msg_t));
137  } catch (Exception &e) {
138  LibLogger::log_warn("PluginNetworkHandler", "Failed to send load failure, exception follows");
139  LibLogger::log_warn("PluginNetworkHandler", e);
140  }
141 }
142 
143 
144 void
145 PluginNetworkHandler::send_load_success(const char *plugin_name, unsigned int client_id)
146 {
147  try {
149  strncpy(r->name, plugin_name, PLUGIN_MSG_NAME_LENGTH);
150  __hub->send(client_id, FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_LOADED,
151  r, sizeof(plugin_loaded_msg_t));
152  } catch (Exception &e) {
153  LibLogger::log_warn("PluginNetworkHandler", "Failed to send load success, exception follows");
154  LibLogger::log_warn("PluginNetworkHandler", e);
155  }
156 }
157 
158 
159 void
160 PluginNetworkHandler::send_unloaded(const char *plugin_name)
161 {
162  __subscribers.lock();
163  try {
164  for (__ssit = __subscribers.begin(); __ssit != __subscribers.end(); ++__ssit) {
165  send_unload_success(plugin_name, *__ssit);
166  }
167  } catch (Exception &e) {
168  LibLogger::log_warn("PluginNetworkHandler", "Failed to send unloaded, exception follows");
169  LibLogger::log_warn("PluginNetworkHandler", e);
170  }
171  __subscribers.unlock();
172 }
173 
174 
175 void
176 PluginNetworkHandler::send_loaded(const char *plugin_name)
177 {
178  __subscribers.lock();
179  try {
180  for (__ssit = __subscribers.begin(); __ssit != __subscribers.end(); ++__ssit) {
181  send_load_success(plugin_name, *__ssit);
182  }
183  } catch (Exception &e) {
184  LibLogger::log_warn("PluginNetworkHandler", "Failed to send loaded, exception follows");
185  LibLogger::log_warn("PluginNetworkHandler", e);
186  }
187  __subscribers.unlock();
188 }
189 
190 
191 void
192 PluginNetworkHandler::send_unload_failure(const char *plugin_name,
193  unsigned int client_id)
194 {
195  try {
197  strncpy(r->name, plugin_name, PLUGIN_MSG_NAME_LENGTH);
198  __hub->send(client_id, FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_UNLOAD_FAILED,
199  r, sizeof(plugin_unload_failed_msg_t));
200  } catch (Exception &e) {
201  LibLogger::log_warn("PluginNetworkHandler", "Failed to send unload failure, exception follows");
202  LibLogger::log_warn("PluginNetworkHandler", e);
203  }
204 }
205 
206 
207 void
208 PluginNetworkHandler::send_unload_success(const char *plugin_name, unsigned int client_id)
209 {
210  try {
212  strncpy(r->name, plugin_name, PLUGIN_MSG_NAME_LENGTH);
213  __hub->send(client_id, FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_UNLOADED,
214  r, sizeof(plugin_unloaded_msg_t));
215  } catch (Exception &e) {
216  LibLogger::log_warn("PluginNetworkHandler", "Failed to send unload success, exception follows");
217  LibLogger::log_warn("PluginNetworkHandler", e);
218  }
219 }
220 
221 
222 
223 /** Load plugin.
224  * The loading is interrupted if any of the plugins does not load properly.
225  * The already loaded plugins are *not* unloaded, but kept.
226  * @param plugin_list string containing a comma-separated list of plugins
227  * to load. The plugin list can contain meta plugins.
228  * @param clid Fawkes network client ID of client that gets a success message
229  * with the exact string that was put into
230  */
231 void
232 PluginNetworkHandler::load(const char *plugin_list, unsigned int clid)
233 {
234  __manager->lock();
235  try {
236  __manager->load(plugin_list);
237  send_load_success(plugin_list, clid);
238  } catch (Exception &e) {
239  LibLogger::log_error("PluginNetworkHandler", "Failed to load plugin %s", plugin_list);
240  LibLogger::log_error("PluginNetworkHandler", e);
241  send_load_failure(plugin_list, clid);
242  }
243  __manager->unlock();
244 }
245 
246 
247 /** Unload plugin.
248  * Note that this method does not allow to pass a list of plugins, but it will
249  * only accept a single plugin at a time.
250  * @param plugin_name plugin to unload, can be a meta plugin.
251  * @param clid Fawkes network client ID of client that gets a success message
252  * with the exact string that was put into
253  */
254 void
255 PluginNetworkHandler::unload(const char *plugin_name, unsigned int clid)
256 {
257  __manager->lock();
258  try {
259  __manager->unload(plugin_name);
260  send_unload_success(plugin_name, clid);
261  } catch (Exception &e) {
262  LibLogger::log_error("PluginNetworkHandler", "Failed to unload plugin %s", plugin_name);
263  LibLogger::log_error("PluginNetworkHandler", e);
264  send_unload_failure(plugin_name, clid);
265  }
266  __manager->unlock();
267 }
268 
269 
270 /** Process all network messages that have been received.
271  */
272 void
274 {
275  while ( ! __inbound_queue.empty() ) {
276  FawkesNetworkMessage *msg = __inbound_queue.front();
277 
278  switch (msg->msgid()) {
279  case MSG_PLUGIN_LOAD:
280  if ( msg->payload_size() != sizeof(plugin_load_msg_t) ) {
281  LibLogger::log_error("PluginNetworkHandler", "Invalid load message size");
282  } else {
284  char name[PLUGIN_MSG_NAME_LENGTH + 1];
285  name[PLUGIN_MSG_NAME_LENGTH] = 0;
286  strncpy(name, m->name, PLUGIN_MSG_NAME_LENGTH);
287 
288  if ( __manager->is_loaded(name) ) {
289  LibLogger::log_info("PluginNetworkHandler", "Client requested loading of %s which is already loaded", name);
290  send_load_success(name, msg->clid());
291  } else {
292  LibLogger::log_info("PluginNetworkHandler", "Loading plugin %s", name);
293  load(name, msg->clid());
294  }
295  }
296  break;
297 
298  case MSG_PLUGIN_UNLOAD:
299  if ( msg->payload_size() != sizeof(plugin_unload_msg_t) ) {
300  LibLogger::log_error("PluginNetworkHandler", "Invalid unload message size.");
301  } else {
303  char name[PLUGIN_MSG_NAME_LENGTH + 1];
304  name[PLUGIN_MSG_NAME_LENGTH] = 0;
305  strncpy(name, m->name, PLUGIN_MSG_NAME_LENGTH);
306 
307  if ( !__manager->is_loaded(name) ) {
308  LibLogger::log_info("PluginNetworkHandler", "Client requested unloading of %s which is not loaded", name);
309  send_unload_success(name, msg->clid());
310  } else {
311  LibLogger::log_info("PluginNetworkHandler", "UNloading plugin %s", name);
312  unload(name, msg->clid());
313  }
314  }
315  break;
316 
318  try {
319  //LibLogger::log_debug("PluginNetworkHandler", "Sending list of all available plugins");
320  PluginListMessage *plm = list_avail();
321  __hub->send(msg->clid(), FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_AVAIL_LIST, plm);
322  } catch (Exception &e) {
323  __hub->send(msg->clid(), FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_AVAIL_LIST_FAILED);
324  }
325  break;
326 
328  try {
329  //LibLogger::log_debug("PluginNetworkHandler", "Sending list of all loaded plugins");
330  PluginListMessage *plm = list_loaded();
331  __hub->send(msg->clid(), FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_LOADED_LIST, plm);
332  } catch (Exception &e) {
333  __hub->send(msg->clid(), FAWKES_CID_PLUGINMANAGER, MSG_PLUGIN_LOADED_LIST_FAILED);
334  }
335  break;
336 
338  __subscribers.lock();
339  __subscribers.push_back(msg->clid());
340  __subscribers.sort();
341  __subscribers.unique();
342  __subscribers.unlock();
343  break;
344 
346  __subscribers.remove_locked(msg->clid());
347  break;
348 
349  default:
350  // error
351  break;
352  }
353 
354  msg->unref();
355  __inbound_queue.pop_locked();
356  }
357 }
358 
359 
360 void
362 {
363  msg->ref();
364  __inbound_queue.push_locked(msg);
365  wakeup();
366 }
367 
368 
369 void
371 {
372 }
373 
374 
375 void
377 {
378  __subscribers.remove_locked(clid);
379 }
380 
381 void
382 PluginNetworkHandler::plugin_loaded(const char *plugin_name)
383 {
384  send_loaded(plugin_name);
385 }
386 
387 void
389 {
390  send_unloaded(plugin_name);
391 }
392 
393 } // end namespace fawkes
void * payload() const
Get payload buffer.
Definition: message.cpp:321
virtual void plugin_unloaded(const char *plugin_name)
Plugin unloaded event.
Definition: handler.cpp:388
request list of available plugins
Definition: messages.h:39
static void log_info(const char *component, const char *format,...)
Log informational message.
Definition: liblogger.cpp:144
plugin unloaded (plugin_unloaded_msg_t)
Definition: messages.h:37
Plugin loaded message.
Definition: messages.h:70
PluginNetworkHandler(PluginManager *manager, FawkesNetworkHub *hub)
Constructor.
Definition: handler.cpp:66
listing available plugins failed
Definition: messages.h:41
Unsubscribe from watching load/unload events.
Definition: messages.h:46
void unref()
Decrement reference count and conditionally delete this instance.
Definition: refcount.cpp:99
virtual void lock() const
Lock list.
Definition: lock_list.h:128
char name[PLUGIN_MSG_NAME_LENGTH]
name of the plugin that has been loaded
Definition: messages.h:71
virtual void loop()
Process all network messages that have been received.
Definition: handler.cpp:273
std::list< std::pair< std::string, std::string > > get_available_plugins()
Generate list of all available plugins.
Definition: manager.cpp:218
Fawkes library namespace.
unsigned int clid() const
Get client ID.
Definition: message.cpp:281
virtual void add_handler(FawkesNetworkHandler *handler)=0
Add a message handler.
Plugin unloaded message.
Definition: messages.h:87
Representation of a message that is sent over the network.
Definition: message.h:75
Thread class encapsulation of pthreads.
Definition: thread.h:42
void remove_locked(const Type &x)
Remove element from list with lock protection.
Definition: lock_list.h:172
~PluginNetworkHandler()
Destructor.
Definition: handler.cpp:80
Fawkes Plugin Manager.
Definition: manager.h:51
char name[PLUGIN_MSG_NAME_LENGTH]
name of the plugin to load.
Definition: messages.h:57
list of loaded plugins (plugin_list_msg_t)
Definition: messages.h:43
plugin unload failed (plugin_unload_failed_msg_t)
Definition: messages.h:38
void lock()
Lock plugin manager.
Definition: manager.cpp:617
virtual void handle_network_message(FawkesNetworkMessage *msg)
Called for incoming messages that are addressed to the correct component ID.
Definition: handler.cpp:361
char name[PLUGIN_MSG_NAME_LENGTH]
name of plugin that could not be unloaded
Definition: messages.h:76
void add_listener(PluginManagerListener *listener)
Add listener.
Definition: manager.cpp:560
Plugin unload failed.
Definition: messages.h:80
static void log_error(const char *component, const char *format,...)
Log error message.
Definition: liblogger.cpp:180
Fawkes Network Hub.
Definition: hub.h:33
plugin loaded (plugin_loaded_msg_t)
Definition: messages.h:34
char name[PLUGIN_MSG_NAME_LENGTH]
name of plugin that could not be unloaded
Definition: messages.h:81
void wakeup()
Wake up thread.
Definition: thread.cpp:1000
request plugin unload (plugin_unload_msg_t)
Definition: messages.h:36
list of available plugins (plugin_list_msg_t)
Definition: messages.h:40
Base class for exceptions in Fawkes.
Definition: exception.h:36
void load(const char *plugin_list)
Load plugin.
Definition: manager.cpp:302
virtual void send(FawkesNetworkMessage *msg)=0
Method to send a message to a specific client.
Plugin list message.
Definition: list_message.h:34
void remove_listener(PluginManagerListener *listener)
Remove listener.
Definition: manager.cpp:573
void ref()
Increment reference count.
Definition: refcount.cpp:70
virtual void unlock() const
Unlock list.
Definition: lock_list.h:144
std::list< std::string > get_loaded_plugins()
Get list of loaded plugins.
Definition: manager.cpp:234
void unload(const char *plugin_name)
Unload plugin.
Definition: manager.cpp:386
const char * name() const
Get name of thread.
Definition: thread.h:95
virtual void plugin_loaded(const char *plugin_name)
Plugin loaded event.
Definition: handler.cpp:382
Network handler abstract base class.
Definition: handler.h:31
listing loaded plugins failed
Definition: messages.h:44
static void log_warn(const char *component, const char *format,...)
Log warning message.
Definition: liblogger.cpp:162
char name[PLUGIN_MSG_NAME_LENGTH]
name of the plugin that has been unloaded
Definition: messages.h:88
virtual void client_disconnected(unsigned int clid)
Called when a client disconnected.
Definition: handler.cpp:376
void pop_locked()
Pop element from queue with lock protection.
Definition: lock_queue.h:149
Load plugin message.
Definition: messages.h:56
void append(const char *plugin_name, size_t len)
Append plugin name.
unsigned short int msgid() const
Get message type ID.
Definition: message.cpp:301
request lif of loaded plugins
Definition: messages.h:42
request plugin load (plugin_load_msg_t)
Definition: messages.h:33
bool is_loaded(const char *plugin_name)
Check if plugin is loaded.
Definition: manager.cpp:258
void push_locked(const Type &x)
Push element to queue with lock protection.
Definition: lock_queue.h:139
Plugin load failed.
Definition: messages.h:75
virtual void client_connected(unsigned int clid)
Called when a new client connected.
Definition: handler.cpp:370
Subscribe for watching load/unload events.
Definition: messages.h:45
plugin load failed (plugin_load_failed_msg_t)
Definition: messages.h:35
void unlock()
Unlock plugin manager.
Definition: manager.cpp:637
virtual void remove_handler(FawkesNetworkHandler *handler)=0
Remove a message handler.
char name[PLUGIN_MSG_NAME_LENGTH]
name of te plugin to unload.
Definition: messages.h:64
size_t payload_size() const
Get payload size.
Definition: message.cpp:311
Unload plugin message.
Definition: messages.h:63