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 <arpa/inet.h>
26 #include <blackboard/internal/instance_factory.h>
27 #include <blackboard/remote.h>
28 #include <interfaces/SwitchInterface.h>
29 #include <sys/mman.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <utils/system/argparser.h>
33 #include <utils/system/fam.h>
34 #include <utils/system/signal.h>
35 #include <utils/time/time.h>
36 
37 #include <cerrno>
38 #include <cstdio>
39 #include <cstdlib>
40 #include <cstring>
41 #include <unistd.h>
42 
43 using namespace fawkes;
44 
45 void
46 print_usage(const char *program_name)
47 {
48  printf("Usage: %s [-h] [-r host:port] <COMMAND> <logfile>\n"
49  " %s print <logfile> <index> [index ...]\n"
50  " %s convert <infile> <outfile> <format>\n"
51  "\n"
52  " -h Print this usage information\n"
53  "COMMANDS:\n"
54  " watch Continuously watch a log file (like tail)\n"
55  " info Print meta information of log file\n"
56  " print Print specific data index\n"
57  " <index> [index ...] is a list of indices to print\n"
58  " replay Replay log file in real-time to console\n"
59  " repair Repair file, i.e. properly set number of entries\n"
60  " enable Enable logging on a remotely running bblogger\n"
61  " disable Disable logging on a remotely running bblogger\n"
62  " convert Convert logfile to different format\n"
63  " <infile> input log file\n"
64  " <outfile> converted output file\n"
65  " <format> format to convert to, currently supported:\n"
66  " - csv Comma-separated values\n",
67  program_name,
68  program_name,
69  program_name);
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 : public FamListener, public SignalHandler
164 {
165 public:
166  BBLogWatcher(const char *filename, BBLogFile &file) : file_(file)
167  {
168  quit_ = false;
169  fam_ = new FileAlterationMonitor();
170  fam_->add_listener(this);
171  fam_->watch_file(filename);
172  SignalManager::register_handler(SIGINT, this);
173  }
174 
175  ~BBLogWatcher()
176  {
178  fam_->remove_listener(this);
179  delete fam_;
180  }
181 
182  virtual void
183  fam_event(const char *filename, unsigned int mask)
184  {
185  if (mask & FAM_DELETE) {
186  quit_ = true;
187  fam_->interrupt();
188  } else {
189  unsigned int remaining = file_.remaining_entries();
190  for (unsigned int i = 0; i < remaining; ++i) {
191  file_.read_next();
192  file_.print_entry();
193  }
194  }
195  }
196 
197  virtual void
198  handle_signal(int signal)
199  {
200  quit_ = true;
201  fam_->interrupt();
202  }
203 
204  void
205  run()
206  {
207  while (!quit_) {
208  fam_->process_events(-1);
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 int
233 set_enabled(const char *hostname, unsigned short int port, bool enabled)
234 {
235  bool rv = 0;
236 
237  BlackBoard * bb = new RemoteBlackBoard(hostname, port);
238  SwitchInterface *si = bb->open_for_reading<SwitchInterface>("BBLogger");
239  if (!si->has_writer()) {
240  printf("No writer exists, BBLogger not loaded?\n");
241  rv = -1;
242  } else {
243  if (enabled) {
245  } else {
247  }
248  }
249 
250  bb->close(si);
251  delete bb;
252  return rv;
253 }
254 
255 /// @endcond
256 
257 void
258 convert_file_csv(BBLogFile &bf, FILE *outf)
259 {
260  fawkes::Interface *iface = bf.interface();
261 
262  // print header row
263  fprintf(outf, "# Time relative to beginning (in sec)");
265  for (i = iface->fields(); i != iface->fields_end(); ++i) {
266  fprintf(outf, ";%s (%s[%zu])", i.get_name(), i.get_typename(), i.get_length());
267  }
268  fprintf(outf, "\n");
269 
270  while (bf.has_next()) {
271  bf.read_next();
272  fprintf(outf, "%f", bf.entry_offset().in_sec());
273 
275  for (i = iface->fields(); i != iface->fields_end(); ++i) {
276  fprintf(outf, ";%s", i.get_value_string());
277  }
278  fprintf(outf, "\n");
279  }
280 }
281 
282 int
283 convert_file(std::string &infile, std::string &outfile, std::string &format)
284 {
285  if (format != "csv") {
286  printf("Unsupported output format '%s'\n", format.c_str());
287  return 8;
288  }
289 
290  FILE *outf = fopen(outfile.c_str(), "wx");
291  if (!outf) {
292  perror("Failed to open output file");
293  return 3;
294  }
295 
296  try {
297  BBLogFile bf(infile.c_str());
298 
299  // Do the conversion!
300  if (format == "csv") {
301  convert_file_csv(bf, outf);
302  }
303 
304  } catch (Exception &e) {
305  printf("Failed to convert log file: %s\n", e.what());
306  e.print_trace();
307  fclose(outf);
308  return 4;
309  }
310 
311  fclose(outf);
312 
313  return 0;
314 }
315 
316 /** BBLogger tool main.
317  * @param argc argument count
318  * @param argv arguments
319  */
320 int
321 main(int argc, char **argv)
322 {
323  ArgumentParser argp(argc, argv, "h");
324 
325  if (argp.has_arg("h")) {
326  print_usage(argv[0]);
327  exit(0);
328  }
329 
330  std::string command, file;
331  if (argp.num_items() < 1) {
332  printf("Invalid number of arguments\n");
333  print_usage(argv[0]);
334  exit(1);
335  } else if (argp.num_items() >= 2) {
336  file = argp.items()[1];
337  }
338 
339  command = argp.items()[0];
340 
341  if (command == "watch") {
342  return watch_file(file);
343 
344  } else if (command == "info") {
345  return print_info(file);
346 
347  } else if (command == "print") {
348  std::vector<const char *> index_strings = argp.items();
349  index_strings.erase(index_strings.begin(), index_strings.begin() + 2);
350 
351  std::vector<unsigned int> indexes(index_strings.size());
352  for (unsigned int i = 0; i < index_strings.size(); ++i) {
353  long l = atol(index_strings[i]);
354  if (l < 0)
355  throw Exception("Invalid index %li", l);
356 
357  indexes[i] = l;
358  }
359 
360  if (indexes.size() == 0) {
361  printf("No indexes given.\n\n");
362  print_usage(argv[0]);
363  exit(6);
364  }
365 
366  return print_indexes(file, indexes);
367 
368  } else if (command == "replay") {
369  return replay_file(file);
370 
371  } else if (command == "repair") {
372  return repair_file(file);
373 
374  } else if ((command == "enable") || (command == "disable")) {
375  char * host = strdup("localhost");
376  unsigned short int port = 1910;
377  if (argp.has_arg("r")) {
378  argp.parse_hostport("r", &host, &port);
379  }
380  int rv = set_enabled(host, port, (command == "enable"));
381  free(host);
382  return rv;
383 
384  } else if (command == "convert") {
385  if (argp.num_items() != 4) {
386  printf("Invalid number of arguments\n");
387  print_usage(argv[0]);
388  exit(7);
389  }
390  std::string outfile = argp.items()[2];
391  std::string format = argp.items()[3];
392  return convert_file(file, outfile, format);
393 
394  } else {
395  printf("Invalid command '%s'\n", command.c_str());
396  print_usage(argv[0]);
397  exit(2);
398  }
399 
400  return 0;
401 }
Interface field iterator.
double in_sec() const
Convet time to seconds.
Definition: time.cpp:219
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:63
A class for handling time.
Definition: time.h:92
virtual const char * what() const
Get primary string.
Definition: exception.cpp:639
Base class for all Fawkes BlackBoard interfaces.
Definition: interface.h:78
bool has_next()
Check if another entry is available.
Definition: bblogfile.cpp:266
void read_next()
Read next entry.
Definition: bblogfile.cpp:283
void wait()
Wait (sleep) for this time.
Definition: time.cpp:736
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:35
void print_info(const char *line_prefix="", FILE *outf=stdout)
Print file meta info.
Definition: bblogfile.cpp:413
const char * get_name() const
Get name of current field.
const char * type_id() const
Get type ID.
Definition: exception.cpp:304
File Alteration Monitor Listener.
Definition: fam.h:35
static void repair_file(const char *filename)
Repair file.
Definition: bblogfile.cpp:323
bool has_writer() const
Check if there is a writer for the interface.
Definition: interface.cpp:814
DisableSwitchMessage Fawkes BlackBoard Interface Message.
void read_index(unsigned int index)
Read entry at particular index.
Definition: bblogfile.cpp:238
fawkes::Interface * interface()
Get interface instance.
Definition: bblogfile.cpp:484
Monitors files for changes.
Definition: fam.h:70
static SignalHandler * register_handler(int signum, SignalHandler *handler)
Register a SignalHandler for a signal.
Definition: signal.cpp:113
size_t get_length() const
Get length of current field.
unsigned int remaining_entries()
Get number of remaining entries.
Definition: bblogfile.cpp:605
unsigned int msgq_enqueue(Message *message)
Enqueue message at end of queue.
Definition: interface.cpp:879
void print_trace()
Prints trace to stderr.
Definition: exception.cpp:601
EnableSwitchMessage Fawkes BlackBoard Interface Message.
InterfaceFieldIterator fields_end()
Invalid iterator.
Definition: interface.cpp:1204
static void unregister_handler(int signum)
Unregister a SignalHandler for a signal.
Definition: signal.cpp:136
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:45
InterfaceFieldIterator fields()
Get iterator over all fields of this interface instance.
Definition: interface.cpp:1195
Class to easily access bblogger log files.
Definition: bblogfile.h:39
virtual void close(Interface *interface)=0
Close interface.