Fawkes API  Fawkes Development Version
bblog.cpp
1 
2 /***************************************************************************
3  * bblog.cpp - BBLogger console tool
4  *
5  * Created: Thu Jan 21 01:33:45 2010
6  * Copyright 2006-2010 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 "../bblogfile.h"
24 
25 #include <utils/system/argparser.h>
26 #include <utils/system/signal.h>
27 #include <utils/time/time.h>
28 #include <utils/system/fam.h>
29 
30 #include <blackboard/remote.h>
31 #include <blackboard/internal/instance_factory.h>
32 #include <interfaces/SwitchInterface.h>
33 
34 #include <cstdlib>
35 #include <cstdio>
36 #include <cerrno>
37 #include <cstring>
38 #include <arpa/inet.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #include <sys/mman.h>
43 
44 using namespace fawkes;
45 
46 void
47 print_usage(const char *program_name)
48 {
49  printf("Usage: %s [-h] [-r host:port] <COMMAND> <logfile>\n"
50  " %s print <logfile> <index> [index ...]\n"
51  " %s convert <infile> <outfile> <format>\n"
52  "\n"
53  " -h Print this usage information\n"
54  "COMMANDS:\n"
55  " watch Continuously watch a log file (like tail)\n"
56  " info Print meta information of log file\n"
57  " print Print specific data index\n"
58  " <index> [index ...] is a list of indices to print\n"
59  " replay Replay log file in real-time to console\n"
60  " repair Repair file, i.e. properly set number of entries\n"
61  " enable Enable logging on a remotely running bblogger\n"
62  " disable Disable logging on a remotely running bblogger\n"
63  " convert Convert logfile to different format\n"
64  " <infile> input log file\n"
65  " <outfile> converted output file\n"
66  " <format> format to convert to, currently supported:\n"
67  " - csv Comma-separated values\n",
68  program_name, program_name, program_name);
69 }
70 
71 
72 int
73 print_info(std::string &filename)
74 {
75  try {
76  BBLogFile bf(filename.c_str());
77  bf.print_info();
78  return 0;
79  } catch (Exception &e) {
80  printf("Failed to print info, exception follows\n");
81  e.print_trace();
82  return -1;
83  }
84 }
85 
86 int
87 repair_file(std::string &filename)
88 {
89  try {
90  BBLogFile::repair_file(filename.c_str());
91  printf("Nothing to repair, files are fine\n");
92  return 0;
93  } catch (Exception &e) {
94  if (strcmp(e.type_id(), "repair-success") == 0) {
95  printf("Repair successful, actions done follow.\n");
96  e.print_trace();
97  return 0;
98  } else {
99  printf("Repair failed, exception follows.\n");
100  e.print_trace();
101  return -1;
102  }
103  }
104 }
105 
106 int
107 print_indexes(std::string &filename, std::vector<unsigned int> &indexes)
108 {
109  try {
110  BBLogFile bf(filename.c_str());
111  for (unsigned int i = 0; i < indexes.size(); ++i) {
112  bf.read_index(indexes[i]);
113  bf.print_entry();
114  }
115  return 0;
116  } catch (Exception &e) {
117  printf("Failed to print info, exception follows\n");
118  e.print_trace();
119  return -1;
120  }
121 
122  return 0;
123 }
124 
125 int
126 replay_file(std::string &filename)
127 {
128  try {
129  BBLogFile bf(filename.c_str());
130 
131  Time last_offset((long)0);
132 
133  if (! bf.has_next()) {
134  printf("File does not have any entries, aborting.\n");
135  return -1;
136  }
137 
138  // print out first immediately, the first offset, usually is a waiting
139  // period until everything was started during logging
140  bf.read_next();
141  bf.print_entry();
142  last_offset = bf.entry_offset();
143 
144  Time diff;
145  while (bf.has_next()) {
146  bf.read_next();
147  diff = bf.entry_offset() - last_offset;
148  diff.wait();
149  last_offset = bf.entry_offset();
150  bf.print_entry();
151  }
152  return 0;
153  } catch (Exception &e) {
154  printf("Failed to print info, exception follows\n");
155  e.print_trace();
156  return -1;
157  }
158 
159  return 0;
160 }
161 /// @cond INTERNAL
162 
163 class BBLogWatcher
164  : public FamListener,
165  public SignalHandler
166 {
167  public:
168  BBLogWatcher(const char *filename, BBLogFile &file)
169  : __file(file)
170  {
171  __quit = false;
172  __fam = new FileAlterationMonitor();
173  __fam->add_listener(this);
174  __fam->watch_file(filename);
175  SignalManager::register_handler(SIGINT, this);
176  }
177 
178  ~BBLogWatcher() {
180  __fam->remove_listener(this);
181  delete __fam;
182  }
183 
184  virtual void fam_event(const char *filename, unsigned int mask)
185  {
186  if (mask & FAM_DELETE) {
187  __quit = true;
188  __fam->interrupt();
189  } else {
190  unsigned int remaining = __file.remaining_entries();
191  for (unsigned int i = 0; i < remaining; ++i) {
192  __file.read_next();
193  __file.print_entry();
194  }
195  }
196  }
197 
198  virtual void handle_signal(int signal)
199  {
200  __quit = true;
201  __fam->interrupt();
202  }
203 
204  void run()
205  {
206  while (! __quit) {
207  __fam->process_events(-1);
208  }
209  }
210 
211 
212  private:
213  bool __quit;
214  FileAlterationMonitor *__fam;
215  BBLogFile &__file;
216 };
217 
218 int
219 watch_file(std::string &filename)
220 {
221  BBLogFile file(filename.c_str(), NULL, false);
222  if (file.remaining_entries() > 0) {
223  // jump to end of file
224  file.read_index(file.remaining_entries() - 1);
225  }
226  BBLogWatcher watcher(filename.c_str(), file);
227  watcher.run();
228 
229  return 0;
230 }
231 
232 
233 int
234 set_enabled(const char *hostname, unsigned short int port, bool enabled)
235 {
236  bool rv = 0;
237 
238  BlackBoard *bb = new RemoteBlackBoard(hostname, port);
239  SwitchInterface *si = bb->open_for_reading<SwitchInterface>("BBLogger");
240  if ( ! si->has_writer() ) {
241  printf("No writer exists, BBLogger not loaded?\n");
242  rv = -1;
243  } else {
244  if (enabled) {
246  } else {
248  }
249  }
250 
251  bb->close(si);
252  delete bb;
253  return rv;
254 }
255 
256 /// @endcond
257 
258 void
259 convert_file_csv(BBLogFile &bf, FILE *outf)
260 {
261  fawkes::Interface *iface = bf.interface();
262 
263  // print header row
264  fprintf(outf, "# Time relative to beginning (in sec)");
266  for (i = iface->fields(); i != iface->fields_end(); ++i) {
267  fprintf(outf, ";%s (%s[%zu])",
268  i.get_name(), i.get_typename(), i.get_length());
269  }
270  fprintf(outf, "\n");
271 
272  while (bf.has_next()) {
273  bf.read_next();
274  fprintf(outf, "%f", bf.entry_offset().in_sec());
275 
277  for (i = iface->fields(); i != iface->fields_end(); ++i) {
278  fprintf(outf, ";%s", i.get_value_string());
279  }
280  fprintf(outf, "\n");
281  }
282 }
283 
284 
285 int
286 convert_file(std::string &infile, std::string &outfile, std::string &format)
287 {
288  if (format != "csv") {
289  printf("Unsupported output format '%s'\n", format.c_str());
290  return 8;
291  }
292 
293  FILE *outf = fopen(outfile.c_str(), "wx");
294  if (!outf) {
295  perror("Failed to open output file");
296  return 3;
297  }
298 
299  try {
300  BBLogFile bf(infile.c_str());
301 
302  // Do the conversion!
303  if (format == "csv") {
304  convert_file_csv(bf, outf);
305  }
306 
307  } catch (Exception &e) {
308  printf("Failed to convert log file: %s\n", e.what());
309  e.print_trace();
310  fclose(outf);
311  return 4;
312  }
313 
314  fclose(outf);
315 
316  return 0;
317 }
318 
319 /** BBLogger tool main.
320  * @param argc argument count
321  * @param argv arguments
322  */
323 int
324 main(int argc, char **argv)
325 {
326  ArgumentParser argp(argc, argv, "h");
327 
328  if ( argp.has_arg("h") ) {
329  print_usage(argv[0]);
330  exit(0);
331  }
332 
333  std::string command, file;
334  if (argp.num_items() < 1) {
335  printf("Invalid number of arguments\n");
336  print_usage(argv[0]);
337  exit(1);
338  } else if (argp.num_items() >= 2) {
339  file = argp.items()[1];
340  }
341 
342  command = argp.items()[0];
343 
344  if (command == "watch") {
345  return watch_file(file);
346 
347  } else if (command == "info") {
348  return print_info(file);
349 
350  } else if (command == "print") {
351  std::vector<const char *> index_strings = argp.items();
352  index_strings.erase(index_strings.begin(), index_strings.begin() + 2);
353 
354  std::vector<unsigned int> indexes(index_strings.size());
355  for (unsigned int i = 0; i < index_strings.size(); ++i) {
356  long l = atol(index_strings[i]);
357  if (l < 0) throw Exception("Invalid index %li", l);
358 
359  indexes[i] = l;
360  }
361 
362  if (indexes.size() == 0) {
363  printf("No indexes given.\n\n");
364  print_usage(argv[0]);
365  exit(6);
366  }
367 
368  return print_indexes(file, indexes);
369 
370  } else if (command == "replay") {
371  return replay_file(file);
372 
373  } else if (command == "repair") {
374  return repair_file(file);
375 
376  } else if ( (command == "enable") || (command == "disable")) {
377  char *host = strdup("localhost");
378  unsigned short int port = 1910;
379  if (argp.has_arg("r")) {
380  argp.parse_hostport("r", &host, &port);
381  }
382  int rv = set_enabled(host, port, (command == "enable"));
383  free(host);
384  return rv;
385 
386  } else if (command == "convert") {
387  if (argp.num_items() != 4) {
388  printf("Invalid number of arguments\n");
389  print_usage(argv[0]);
390  exit(7);
391  }
392  std::string outfile = argp.items()[2];
393  std::string format = argp.items()[3];
394  return convert_file(file, outfile, format);
395 
396  } else {
397  printf("Invalid command '%s'\n", command.c_str());
398  print_usage(argv[0]);
399  exit(2);
400  }
401 
402  return 0;
403 }
Interface field iterator.
double in_sec() const
Convet time to seconds.
Definition: time.cpp:232
const char * get_typename() const
Get type of current field as string.
Fawkes library namespace.
Interface for signal handling.
Definition: signal.h:35
Parse command line arguments.
Definition: argparser.h:66
A class for handling time.
Definition: time.h:91
virtual const char * what() const
Get primary string.
Definition: exception.cpp:661
Base class for all Fawkes BlackBoard interfaces.
Definition: interface.h:79
bool has_next()
Check if another entry is available.
Definition: bblogfile.cpp:267
void read_next()
Read next entry.
Definition: bblogfile.cpp:284
void wait()
Wait (sleep) for this time.
Definition: time.cpp:817
const fawkes::Time & entry_offset() const
Get current entry offset.
Definition: bblogfile.cpp:514
SwitchInterface Fawkes BlackBoard Interface.
Base class for exceptions in Fawkes.
Definition: exception.h:36
void print_info(const char *line_prefix="", FILE *outf=stdout)
Print file meta info.
Definition: bblogfile.cpp:415
const char * get_name() const
Get name of current field.
const char * type_id() const
Get type ID.
Definition: exception.cpp:311
File Alteration Monitor Listener.
Definition: fam.h:35
static void repair_file(const char *filename)
Repair file.
Definition: bblogfile.cpp:327
bool has_writer() const
Check if there is a writer for the interface.
Definition: interface.cpp:834
DisableSwitchMessage Fawkes BlackBoard Interface Message.
void read_index(unsigned int index)
Read entry at particular index.
Definition: bblogfile.cpp:237
fawkes::Interface * interface()
Get interface instance.
Definition: bblogfile.cpp:480
Monitors files for changes.
Definition: fam.h:71
static SignalHandler * register_handler(int signum, SignalHandler *handler)
Register a SignalHandler for a signal.
Definition: signal.cpp:116
size_t get_length() const
Get length of current field.
unsigned int remaining_entries()
Get number of remaining entries.
Definition: bblogfile.cpp:614
unsigned int msgq_enqueue(Message *message)
Enqueue message at end of queue.
Definition: interface.cpp:903
void print_trace()
Prints trace to stderr.
Definition: exception.cpp:619
EnableSwitchMessage Fawkes BlackBoard Interface Message.
InterfaceFieldIterator fields_end()
Invalid iterator.
Definition: interface.cpp:1218
static void unregister_handler(int signum)
Unregister a SignalHandler for a signal.
Definition: signal.cpp:140
Remote BlackBoard.
Definition: remote.h:48
virtual Interface * open_for_reading(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for reading.
The BlackBoard abstract class.
Definition: blackboard.h:48
InterfaceFieldIterator fields()
Get iterator over all fields of this interface instance.
Definition: interface.cpp:1208
Class to easily access bblogger log files.
Definition: bblogfile.h:38
virtual void close(Interface *interface)=0
Close interface.