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  */
61 PluginNetworkHandler::PluginNetworkHandler(PluginManager *manager, FawkesNetworkHub *hub)
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 {
271  plugin_load_msg_t *m = (plugin_load_msg_t *)msg->payload();
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 {
292  plugin_unload_msg_t *m = (plugin_unload_msg_t *)msg->payload();
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
350 PluginNetworkHandler::handle_network_message(FawkesNetworkMessage *msg)
351 {
352  msg->ref();
353  inbound_queue_.push_locked(msg);
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
375 PluginNetworkHandler::plugin_unloaded(const char *plugin_name)
376 {
377  send_unloaded(plugin_name);
378 }
379 
380 } // end namespace fawkes
fawkes::LockList::lock
virtual void lock() const
Lock list.
Definition: lock_list.h:128
fawkes::PluginManager::is_loaded
bool is_loaded(const std::string &plugin_name)
Check if plugin is loaded.
Definition: manager.cpp:261
fawkes::MSG_PLUGIN_UNLOAD_FAILED
plugin unload failed (plugin_unload_failed_msg_t)
Definition: messages.h:47
fawkes::LockQueue::push_locked
void push_locked(const Type &x)
Push element to queue with lock protection.
Definition: lock_queue.h:139
fawkes::PluginManager::unlock
void unlock()
Unlock plugin manager.
Definition: manager.cpp:684
fawkes::PluginManager::lock
void lock()
Lock plugin manager.
Definition: manager.cpp:665
fawkes::MSG_PLUGIN_LOADED_LIST
list of loaded plugins (plugin_list_msg_t)
Definition: messages.h:52
fawkes::plugin_unload_msg_t::name
char name[PLUGIN_MSG_NAME_LENGTH]
name of te plugin to unload.
Definition: messages.h:69
fawkes::FawkesNetworkHub::send
virtual void send(FawkesNetworkMessage *msg)=0
fawkes::Thread::wakeup
void wakeup()
Wake up thread.
Definition: thread.cpp:999
fawkes::LibLogger::log_error
static void log_error(const char *component, const char *format,...)
Log error message.
Definition: liblogger.cpp:178
fawkes::MSG_PLUGIN_LIST_AVAIL
request list of available plugins
Definition: messages.h:48
fawkes::PluginNetworkHandler::loop
virtual void loop()
Process all network messages that have been received.
Definition: handler.cpp:265
fawkes::PluginManager::remove_listener
void remove_listener(PluginManagerListener *listener)
Remove listener.
Definition: manager.cpp:620
fawkes::plugin_load_msg_t
Load plugin message.
Definition: messages.h:59
fawkes::PluginManager::get_available_plugins
std::list< std::pair< std::string, std::string > > get_available_plugins()
Generate list of all available plugins.
Definition: manager.cpp:222
fawkes::PluginNetworkHandler::PluginNetworkHandler
PluginNetworkHandler(PluginManager *manager, FawkesNetworkHub *hub)
Constructor.
Definition: handler.cpp:65
fawkes::Thread::name
const char * name() const
Definition: thread.h:99
fawkes::PluginManager::get_loaded_plugins
std::list< std::string > get_loaded_plugins()
Get list of loaded plugins.
Definition: manager.cpp:238
fawkes::MSG_PLUGIN_UNLOAD
request plugin unload (plugin_unload_msg_t)
Definition: messages.h:45
fawkes::MSG_PLUGIN_UNSUBSCRIBE_WATCH
Unsubscribe from watching load/unload events.
Definition: messages.h:55
fawkes::MSG_PLUGIN_AVAIL_LIST_FAILED
listing available plugins failed
Definition: messages.h:50
fawkes::MSG_PLUGIN_LOADED
plugin loaded (plugin_loaded_msg_t)
Definition: messages.h:43
fawkes::PluginNetworkHandler::~PluginNetworkHandler
~PluginNetworkHandler()
Destructor.
Definition: handler.cpp:77
fawkes::MSG_PLUGIN_AVAIL_LIST
list of available plugins (plugin_list_msg_t)
Definition: messages.h:49
fawkes::LockList::unlock
virtual void unlock() const
Unlock list.
Definition: lock_list.h:142
fawkes::LockQueue::pop_locked
void pop_locked()
Pop element from queue with lock protection.
Definition: lock_queue.h:148
fawkes::PluginNetworkHandler::handle_network_message
virtual void handle_network_message(FawkesNetworkMessage *msg)
Definition: handler.cpp:354
fawkes::PluginNetworkHandler::plugin_loaded
virtual void plugin_loaded(const char *plugin_name)
Definition: handler.cpp:373
fawkes::MSG_PLUGIN_LIST_LOADED
request lif of loaded plugins
Definition: messages.h:51
fawkes::LibLogger::log_warn
static void log_warn(const char *component, const char *format,...)
Log warning message.
Definition: liblogger.cpp:160
fawkes::PluginNetworkHandler::client_connected
virtual void client_connected(unsigned int clid)
Definition: handler.cpp:362
fawkes
fawkes::PluginManager::load
void load(const std::string &plugin_list)
Load plugin.
Definition: manager.cpp:329
fawkes::MSG_PLUGIN_LOAD_FAILED
plugin load failed (plugin_load_failed_msg_t)
Definition: messages.h:44
fawkes::PluginListMessage::append
void append(const char *plugin_name, size_t len)
Append plugin name.
Definition: list_message.cpp:92
fawkes::PluginNetworkHandler::plugin_unloaded
virtual void plugin_unloaded(const char *plugin_name)
Definition: handler.cpp:379
fawkes::PluginListMessage
Definition: list_message.h:38
fawkes::MSG_PLUGIN_LOAD
request plugin load (plugin_load_msg_t)
Definition: messages.h:42
fawkes::plugin_unload_msg_t
Unload plugin message.
Definition: messages.h:67
fawkes::MSG_PLUGIN_LOADED_LIST_FAILED
listing loaded plugins failed
Definition: messages.h:53
fawkes::MSG_PLUGIN_UNLOADED
plugin unloaded (plugin_unloaded_msg_t)
Definition: messages.h:46
fawkes::FawkesNetworkHub::remove_handler
virtual void remove_handler(FawkesNetworkHandler *handler)=0
fawkes::LockList::remove_locked
void remove_locked(const Type &x)
Remove element from list with lock protection.
Definition: lock_list.h:167
fawkes::PluginManager::unload
void unload(const std::string &plugin_name)
Unload plugin.
Definition: manager.cpp:431
fawkes::PluginNetworkHandler::client_disconnected
virtual void client_disconnected(unsigned int clid)
Definition: handler.cpp:367
fawkes::MSG_PLUGIN_SUBSCRIBE_WATCH
Subscribe for watching load/unload events.
Definition: messages.h:54
fawkes::plugin_load_msg_t::name
char name[PLUGIN_MSG_NAME_LENGTH]
name of the plugin to load.
Definition: messages.h:61
fawkes::LibLogger::log_info
static void log_info(const char *component, const char *format,...)
Log informational message.
Definition: liblogger.cpp:142
fawkes::Exception
Definition: exception.h:39