Fawkes API  Fawkes Development Version
blackboard_processor.cpp
1 
2 /***************************************************************************
3  * blackboard_processor.cpp - Web request processor for BlackBoard info
4  *
5  * Created: Thu Oct 23 16:10:21 2008
6  * Copyright 2006-2008 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.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Library General Public License for more details.
19  *
20  * Read the full text in the LICENSE.GPL file in the doc directory.
21  */
22 
23 #include "blackboard_processor.h"
24 #include <webview/page_reply.h>
25 #include <webview/file_reply.h>
26 #include <webview/error_reply.h>
27 
28 #include <blackboard/blackboard.h>
29 #include <interface/interface.h>
30 #include <interface/field_iterator.h>
31 #include <interface/interface_info.h>
32 #include <utils/time/time.h>
33 #include <utils/misc/string_split.h>
34 
35 #include <string>
36 #include <cstring>
37 #include <cstdlib>
38 
39 #include <set>
40 #include <sstream>
41 #include <algorithm>
42 #ifdef HAVE_GRAPHVIZ
43 # include <gvc.h>
44 # include <gvcjob.h>
45 #endif
46 
47 
48 using namespace fawkes;
49 
50 /** @class WebviewBlackBoardRequestProcessor "blackboard_processor.h"
51  * BlackBoard web request processor.
52  * Provides access to BlackBoard introspection features.
53  * @author Tim Niemueller
54  */
55 
56 /** Constructor.
57  * @param baseurl Base URL where processor is mounted
58  * @param blackboard BlackBoard instance
59  */
61  BlackBoard *blackboard)
62 {
63  __baseurl = strdup(baseurl);
64  __baseurl_len = strlen(__baseurl);
65  __blackboard = blackboard;
66 }
67 
68 
69 /** Destructor. */
71 {
72  free(__baseurl);
73  for (__ifi = __interfaces.begin(); __ifi != __interfaces.end(); ++__ifi) {
74  __blackboard->close(__ifi->second);
75  }
76  __interfaces.clear();
77 }
78 
79 
80 WebReply *
82 {
83  if ( strncmp(__baseurl, request->url().c_str(), __baseurl_len) == 0 ) {
84  // It is in our URL prefix range
85  std::string subpath = request->url().substr(__baseurl_len);
86 
87  if (subpath.find("/graph/graph.png") == 0) {
88 #if defined(HAVE_GRAPHVIZ) && ((defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))) || defined(__clang__))
89  std::string graph_node = request->get_value("for");
90  std::string graph = generate_graph(graph_node);
91 
92  FILE *f = tmpfile();
93  if (NULL == f) {
95  "Cannot open temp file: %s", strerror(errno));
96  }
97 
98  GVC_t* gvc = gvContext();
99  Agraph_t* G = agmemread((char *)graph.c_str());
100  gvLayout(gvc, G, (char *)"dot");
101  gvRender(gvc, G, (char *)"png", f);
102  gvFreeLayout(gvc, G);
103  agclose(G);
104  gvFreeContext(gvc);
105 
106  try {
107  DynamicFileWebReply *freply = new DynamicFileWebReply(f);
108  return freply;
109  } catch (fawkes::Exception &e) {
111  }
112 #else
114  "BlackBoard processor was not built with Graphviz support");
115 #endif
116  } else {
117 
118  WebPageReply *r = new WebPageReply("BlackBoard");
119  r->set_html_header(" <link type=\"text/css\" href=\""
120  "/static/css/jqtheme/jquery-ui.custom.css\" rel=\"stylesheet\" />\n"
121  " <link type=\"text/css\" href=\""
122  "/static/css/blackboard.css\" rel=\"stylesheet\" />\n");
123 
124 
125  if (subpath.find("/view/") != 0 && subpath.find("/graph") != 0) {
126  *r += "\n\n<h2>Select Interface</h2>\n"
127  "<div id=\"blackboard-interfaces-mainpart\">\n";
128  } else {
129  *r += "\n\n <div id=\"blackboard-interfaces\">\n";
130  }
131 
132  bool found_some = false;
133  InterfaceInfoList *iil = __blackboard->list_all();
134  iil->sort();
135  for (InterfaceInfoList::iterator i = iil->begin(); i != iil->end(); ++i) {
136  if (! found_some) {
137  *r += "<table>\n";
138  *r += "<tr><th>Interface</th><th>Reader(s)</th><th>Writer</th></tr>\n";
139  found_some = true;
140  }
141  r->append_body("<tr><td><a href=\"%s/view/%s::%s\">%s::%s</a></td><td>%u</td><td style=\"color:%s\">%s</td></tr>\n",
142  __baseurl, i->type(), i->id(), i->type(), i->id(),
143  i->num_readers(), i->has_writer() ? "green" : "red", i->has_writer() ? i->writer().c_str() : "no");
144  }
145  delete iil;
146 
147  if (found_some) {
148  *r += "</table>\n";
149  }
150 
151 #ifdef HAVE_GRAPHVIZ
152  if (subpath.find("/graph") != 0) {
153  r->append_body(" <div class=\"blackboard-graph-div\">"
154  "<a href=\"%s/graph\" class=\"blackboard-graph-link\">Graph</a></div>\n", __baseurl);
155  }
156 #endif
157 
158  *r += " </div>\n";
159 
160  if (! found_some) {
161  *r += "<p><b>No interfaces found.</b></p>\n";
162  }
163 
164  if (subpath.find("/view/") == 0) {
165  std::string iuid = subpath.substr(subpath.find_first_not_of("/", std::string("/view/").length()));
166  std::string iftype = iuid.substr(0, iuid.find("::"));
167  std::string ifname = iuid.substr(iuid.find("::") + 2);
168 
169  r->append_body("<h2>Showing %s</h2>\n", iuid.c_str());
170  if (__interfaces.find(iuid) == __interfaces.end()) {
171  try {
172  Interface *iface = __blackboard->open_for_reading(iftype.c_str(), ifname.c_str());
173  __interfaces[iuid] = iface;
174  } catch (Exception &e) {
175  r->append_body("Failed to open interface: %s\n", e.what());
176  }
177  }
178  if (__interfaces.find(iuid) != __interfaces.end()) {
179  Interface *iface = __interfaces[iuid];
180  iface->read();
181 
182  /*
183  *r += "<script type=\"text/javascript\">\n"
184  " $(function(){\n"
185  " $(\"#blackboard-interface-details-title\").click(function(){\n"
186  " if ( $(\"#blackboard-interface-details\").is(\":visible\") ) {\n"
187  " $(\"#blackboard-interface-details\").hide(\"blind\");\n"
188  " $(\"#blackboard-interfaces-icon\").attr(\"src\", "
189  "\"/static/images/icon-triangle-e.png\");\n"
190  " } else {\n"
191  " $(\"#blackboard-interface-details\").show(\"blind\");\n"
192  " $(\"#blackboard-interfaces-icon\").attr(\"src\", "
193  "\"/static/images/icon-triangle-s.png\");\n"
194  " }\n"
195  " });\n"
196  " $(\"#blackboard-interface-details\").hide();\n"
197  " });\n"
198  "</script>\n"
199  "<div id=\"blackboard-box\">\n"
200  " <div><a id=\"blackboard-interface-details-title\" href=\"#\">"
201  "<img id=\"blackboard-interfaces-icon\" "
202  "class=\"blackboard-interfaces-icon\" "
203  "src=\"/static/images/icon-triangle-e.png\" />"
204  "Interface details</a></div>\n"
205  " <div id=\"blackboard-interface-details\">\n";
206  */
207 
208  std::string writer;
209  if (iface->has_writer()) {
210  try {
211  writer = iface->writer();
212  } catch (Exception &e) {}
213  }
214  std::string readers;
215  try {
216  readers = str_join(iface->readers(), ", ");
217  } catch (Exception &e) {}
218 
219  r->append_body("<table>\n"
220  " <tr><td><b>Type:</b></td><td>%s</td></tr>\n"
221  " <tr><td><b>ID:</b></td><td>%s</td></tr>\n"
222  " <tr><td><b>Writer:</b></td><td><span class=\"blackboard-writer-%s\">%s</span></td></tr>\n"
223  " <tr><td><b>Readers:</b></td><td>%s (%u)</td></tr>\n"
224  " <tr><td><b>Serial:</b></td><td>%u</td></tr>\n"
225  " <tr><td><b>Data size:</b></td><td>%u</td></tr>\n"
226  " <tr><td><b>Hash:</b></td><td>%s</td></tr>\n"
227  " <tr><td><b>Data changed:</b></td>"
228  "<td>%s (last at %s)</td></tr>\n"
229  "</table>\n",
230  iface->type(), iface->id(),
231  iface->has_writer() ? "exists" : "none",
232  iface->has_writer() ? writer.c_str() : "none",
233  iface->num_readers() > 0 ? readers.c_str() : "none",
234  iface->num_readers(),
235  iface->serial(),
236  iface->datasize(), iface->hash_printable(),
237  iface->changed() ? "yes" : "no", iface->timestamp()->str());
238 
239  /*
240  *r += " </div>\n"
241  "</div>\n";
242  */
243 
244  r->append_body("<table>\n"
245  " <tr>\n"
246  " <th>Name</th><th>Type</th><th>Value</th>\n"
247  " </tr>\n");
248  for (InterfaceFieldIterator fi = iface->fields(); fi != iface->fields_end(); ++fi) {
249  bool is_string = (fi.get_type() == IFT_STRING);
250  *r += " <tr>\n";
251  if ( fi.get_length() > 1 ) {
252  r->append_body(" <td>%s</td><td>%s [%zu]</td><td>%s%s%s</td>\n",
253  fi.get_name(), fi.get_typename(),
254  fi.get_length(), is_string ? "<pre>" : "",
255  fi.get_value_string(), is_string ? "</pre>" : "");
256  } else {
257  r->append_body(" <td>%s</td><td>%s</td><td>%s%s%s</td>\n",
258  fi.get_name(), fi.get_typename(), is_string ? "<pre>" : "",
259  fi.get_value_string(), is_string ? "</pre>" : "");
260  }
261  *r += " </tr>\n";
262  }
263  r->append_body("</table>\n");
264  r->append_body("<p><a href=\"%s\">Clear detailed</a></p>\n", __baseurl);
265  }
266  } else if (subpath.find("/graph") == 0) {
267  std::string graph_baseurl("/graph/");
268  std::string graph_node =
269  subpath.length() > graph_baseurl.length() ? subpath.substr(graph_baseurl.length()) : "";
270 #if defined(HAVE_GRAPHVIZ) && ((defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))) || defined(__clang__))
271  std::string graph = generate_graph(graph_node);
272  char *map;
273  unsigned int map_length;
274 
275  GVC_t* gvc = gvContext();
276  Agraph_t* G = agmemread((char *)graph.c_str());
277  gvLayout(gvc, G, (char *)"dot");
278  gvRenderData(gvc, G, (char *)"cmapx", &map, &map_length);
279  r->append_body("\n%s\n", map);
280 #if GRAPHVIZ_VERSION >= 23200
281  gvFreeRenderData(map);
282 #else
283  free(map);
284 #endif
285  gvFreeLayout(gvc, G);
286  agclose(G);
287  gvFreeContext(gvc);
288 
289  r->append_body("<p><img src=\"%s/graph/graph.png%s%s\" usemap=\"#bbmap\" /></p>\n",
290  __baseurl,
291  graph_node.empty() ? "" : "?for=",
292  graph_node.empty() ? "" : graph_node.c_str());
293  r->append_body("<!-- DOT Graph:\n\n%s\n\n-->\n\n", graph.c_str());
294 #else
295  r->append_body("<p>No graphviz support at compile time</p>\n");
296 #endif
297  if (! graph_node.empty()) {
298  r->append_body("<p><a href=\"%s/graph\">Full Graph</a></p>\n\n", __baseurl);
299  }
300  }
301  return r;
302  }
303 
304  } else {
305  return NULL;
306  }
307 }
308 
309 #if defined(HAVE_GRAPHVIZ) && ((defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))) || defined(__clang__))
310 std::string
311 WebviewBlackBoardRequestProcessor::generate_graph(std::string for_owner)
312 {
313  InterfaceInfoList *iil = __blackboard->list_all();
314  iil->sort();
315 
316  std::stringstream mstream;
317  mstream << "digraph bbmap {" << std::endl
318  << " graph [fontsize=12,rankdir=LR];" << std::endl;
319 
320  std::set<std::string> owners;
321 
322  InterfaceInfoList::iterator ii;
323  for (ii = iil->begin(); ii != iil->end(); ++ii) {
324  const std::list<std::string> readers = ii->readers();
325 
326  if (for_owner == "" ||
327  ii->writer() == for_owner ||
328  std::find_if(readers.begin(), readers.end(),
329  [&for_owner](const std::string &o)->bool { return for_owner == o; })
330  != readers.end())
331  {
332  if (ii->has_writer()) {
333  const std::string writer = ii->writer();
334  if (! writer.empty()) owners.insert(writer);
335  }
336  std::list<std::string>::const_iterator r;
337  for (r = readers.begin(); r != readers.end(); ++r) {
338  owners.insert(*r);
339  }
340  }
341  }
342 
343  mstream << " node [fontsize=12 shape=box width=4 margin=0.05];" << std::endl
344  << " { rank=same; " << std::endl;
345  std::set<std::string>::iterator i;
346  for (ii = iil->begin(); ii != iil->end(); ++ii) {
347  const std::list<std::string> readers = ii->readers();
348  if (for_owner == "" ||
349  ii->writer() == for_owner ||
350  std::find_if(readers.begin(), readers.end(),
351  [&for_owner](const std::string &o)->bool { return for_owner == o; })
352  != readers.end())
353  {
354  mstream << " \"" << ii->type() << "::" << ii->id() << "\""
355  << " [href=\"" << __baseurl << "/view/" << ii->type() << "::" << ii->id() << "\"";
356 
357 
358  if (! ii->has_writer()) {
359  mstream << " color=red";
360  } else if (ii->writer().empty()) {
361  mstream << " color=purple";
362  }
363  mstream << "];" << std::endl;
364  }
365  }
366  mstream << " }" << std::endl;
367 
368  mstream << " node [fontsize=12 shape=octagon width=3];" << std::endl;
369  for (i = owners.begin(); i != owners.end(); ++i) {
370  mstream << " \"" << *i << "\""
371  << " [href=\"" << __baseurl << "/graph/" << *i << "\"];"
372  << std::endl;
373  }
374 
375  for (ii = iil->begin(); ii != iil->end(); ++ii) {
376  const std::list<std::string> readers = ii->readers();
377  if (for_owner == "" ||
378  ii->writer() == for_owner ||
379  std::find_if(readers.begin(), readers.end(),
380  [&for_owner](const std::string &o)->bool { return for_owner == o; })
381  != readers.end())
382  {
383  std::list<std::string> quoted_readers;
384  std::for_each(readers.begin(), readers.end(),
385  [&quoted_readers](const std::string &r) {
386  quoted_readers.push_back(std::string("\"")+r+"\"");
387  });
388  std::string quoted_readers_s = str_join(quoted_readers, ' ');
389  mstream << " \"" << ii->type() << "::" << ii->id() << "\" -> { "
390  << quoted_readers_s << " } [style=dashed arrowhead=dot arrowsize=0.5 dir=both];" << std::endl;
391 
392  if (ii->has_writer()) {
393  mstream << " \"" << (ii->writer().empty() ? "???" : ii->writer()) << "\" -> \""
394  << ii->type() << "::" << ii->id() << "\""
395  << (ii->writer().empty() ? " [color=purple]" : " [color=\"#008800\"]")
396  << ";" << std::endl;
397  }
398  }
399  }
400 
401  delete iil;
402 
403  mstream << "}";
404  return mstream.str();
405 }
406 #endif
Interface field iterator.
virtual void set_html_header(std::string h)
Set HTML header text.
Definition: page_reply.cpp:94
std::string writer() const
Get owner name of writing interface instance.
Definition: interface.cpp:874
unsigned int datasize() const
Get data size.
Definition: interface.cpp:534
const char * str(bool utc=false) const
Output function.
Definition: time.cpp:872
Dynamic raw file transfer reply.
Definition: file_reply.h:34
virtual ~WebviewBlackBoardRequestProcessor()
Destructor.
Fawkes library namespace.
WebviewBlackBoardRequestProcessor(const char *baseurl, fawkes::BlackBoard *blackboard)
Constructor.
const char * id() const
Get identifier of interface.
Definition: interface.cpp:661
string field
Definition: types.h:47
virtual const char * what() const
Get primary string.
Definition: exception.cpp:661
Base class for all Fawkes BlackBoard interfaces.
Definition: interface.h:79
virtual fawkes::WebReply * process_request(const fawkes::WebRequest *request)
Process a request.
Interface information list.
const char * type() const
Get type of interface.
Definition: interface.cpp:651
Base class for exceptions in Fawkes.
Definition: exception.h:36
unsigned short serial() const
Get instance serial of interface.
Definition: interface.cpp:697
void read()
Read from BlackBoard into local copy.
Definition: interface.cpp:477
std::string get_value(std::string &key) const
Get specific GET value.
Definition: request.h:151
static std::string str_join(const std::vector< std::string > &v, char delim='/')
Join vector of strings string using given delimiter.
Definition: string_split.h:96
bool has_writer() const
Check if there is a writer for the interface.
Definition: interface.cpp:834
iterator begin()
Get iterator for messages.
Definition: exception.cpp:700
Web request meta data carrier.
Definition: request.h:42
Basic page reply.
Definition: page_reply.h:36
const Time * timestamp() const
Get timestamp of last write.
Definition: interface.cpp:718
bool changed() const
Check if data has been changed.
Definition: interface.cpp:796
Basic web reply.
Definition: reply.h:36
InterfaceFieldIterator fields_end()
Invalid iterator.
Definition: interface.cpp:1218
void append_body(const char *format,...)
Append to body.
Definition: reply.cpp:224
const std::string & url() const
Get URL.
Definition: request.h:69
unsigned int num_readers() const
Get the number of readers.
Definition: interface.cpp:863
The BlackBoard abstract class.
Definition: blackboard.h:48
InterfaceFieldIterator fields()
Get iterator over all fields of this interface instance.
Definition: interface.cpp:1208
Static error page reply.
Definition: error_reply.h:33
std::list< std::string > readers() const
Get owner names of reading interface instances.
Definition: interface.cpp:884
const char * hash_printable() const
Get printable interface hash.
Definition: interface.cpp:304