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