bblog.cpp

00001 
00002 /***************************************************************************
00003  *  bblog.cpp - BBLogger console tool
00004  *
00005  *  Created: Thu Jan 21 01:33:45 2010
00006  *  Copyright  2006-2010  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU Library General Public License for more details.
00019  *
00020  *  Read the full text in the LICENSE.GPL file in the doc directory.
00021  */
00022 
00023 #include "../bblogfile.h"
00024 
00025 #include <utils/system/argparser.h>
00026 #include <utils/system/signal.h>
00027 #include <utils/time/time.h>
00028 #include <utils/system/fam.h>
00029 
00030 #include <blackboard/remote.h>
00031 #include <blackboard/internal/instance_factory.h>
00032 #include <interfaces/SwitchInterface.h>
00033 
00034 #include <cstdlib>
00035 #include <cstdio>
00036 #include <cerrno>
00037 #include <cstring>
00038 #include <arpa/inet.h>
00039 #include <sys/types.h>
00040 #include <sys/stat.h>
00041 #include <unistd.h>
00042 #include <sys/mman.h>
00043 
00044 using namespace fawkes;
00045 
00046 void
00047 print_usage(const char *program_name)
00048 {
00049   printf("Usage: %s [-h] [-r host:port] <COMMAND> <logfile>\n"
00050          "       %s print <logfile> <index> [index ...]\n"
00051          "       %s convert <infile> <outfile> <format>\n"
00052          "\n"
00053          " -h  Print this usage information\n"
00054          "COMMANDS:\n"
00055          " watch     Continuously watch a log file (like tail)\n"
00056          " info      Print meta information of log file\n"
00057          " print     Print specific data index\n"
00058          "           <index> [index ...] is a list of indices to print\n"
00059          " replay    Replay log file in real-time to console\n"
00060          " repair    Repair file, i.e. properly set number of entries\n"
00061          " enable    Enable logging on a remotely running bblogger\n"
00062          " disable   Disable logging on a remotely running bblogger\n"
00063          " convert   Convert logfile to different format\n"
00064          "           <infile>  input log file\n"
00065          "           <outfile> converted output file\n"
00066          "           <format>  format to convert to, currently supported:\n"
00067          "             - csv  Comma-separated values\n",
00068          program_name, program_name, program_name);
00069 }
00070 
00071 
00072 int
00073 print_info(std::string &filename)
00074 {
00075   try {
00076     BBLogFile bf(filename.c_str());
00077     bf.print_info();
00078     return 0;
00079   } catch (Exception &e) {
00080     printf("Failed to print info, exception follows\n");
00081     e.print_trace();
00082     return -1;
00083   }
00084 }
00085 
00086 int
00087 repair_file(std::string &filename)
00088 {
00089   try {
00090     BBLogFile::repair_file(filename.c_str());
00091     printf("Nothing to repair, files are fine\n");
00092     return 0;
00093   } catch (Exception &e) {
00094     if (strcmp(e.type_id(), "repair-success") == 0) {
00095       printf("Repair successful, actions done follow.\n");
00096       e.print_trace();
00097       return 0;
00098     } else {
00099       printf("Repair failed, exception follows.\n");
00100       e.print_trace();
00101       return -1;
00102     }
00103   }
00104 }
00105 
00106 int
00107 print_indexes(std::string &filename, std::vector<unsigned int> &indexes)
00108 {
00109   try {
00110     BBLogFile bf(filename.c_str());
00111     for (unsigned int i = 0; i < indexes.size(); ++i) {
00112       bf.read_index(indexes[i]);
00113       bf.print_entry();
00114     }
00115     return 0;
00116   } catch (Exception &e) {
00117     printf("Failed to print info, exception follows\n");
00118     e.print_trace();
00119     return -1;
00120   }
00121 
00122   return 0;  
00123 }
00124 
00125 int
00126 replay_file(std::string &filename)
00127 {
00128   try {
00129     BBLogFile bf(filename.c_str());
00130 
00131     Time last_offset((long)0);
00132 
00133     if (! bf.has_next()) {
00134       printf("File does not have any entries, aborting.\n");
00135       return -1;
00136     }
00137 
00138     // print out first immediately, the first offset, usually is a waiting
00139     // period until everything was started during logging
00140     bf.read_next();
00141     bf.print_entry();
00142     last_offset = bf.entry_offset();
00143 
00144     Time diff;
00145     while (bf.has_next()) {
00146       bf.read_next();
00147       diff = bf.entry_offset() - last_offset;
00148       diff.wait();
00149       last_offset = bf.entry_offset();
00150       bf.print_entry();
00151     }
00152     return 0;
00153   } catch (Exception &e) {
00154     printf("Failed to print info, exception follows\n");
00155     e.print_trace();
00156     return -1;
00157   }
00158 
00159   return 0;  
00160 }
00161 /// @cond INTERNAL
00162 
00163 class BBLogWatcher
00164   : public FamListener,
00165     public SignalHandler
00166 {
00167  public:
00168   BBLogWatcher(const char *filename, BBLogFile &file)
00169     : __file(file)
00170   {
00171     __quit = false;
00172     __fam = new FileAlterationMonitor();
00173     __fam->add_listener(this);
00174     __fam->watch_file(filename);
00175     SignalManager::register_handler(SIGINT, this);
00176   }
00177 
00178   ~BBLogWatcher() {
00179     SignalManager::unregister_handler(this);
00180     __fam->remove_listener(this);
00181     delete __fam;
00182   }
00183 
00184   virtual void fam_event(const char *filename, unsigned int mask)
00185   {
00186     if (mask & FAM_DELETE) {
00187       __quit = true;
00188       __fam->interrupt();
00189     } else {
00190       unsigned int remaining = __file.remaining_entries();
00191       for (unsigned int i = 0; i < remaining; ++i) {
00192         __file.read_next();
00193         __file.print_entry();
00194       }
00195     }
00196   }
00197 
00198   virtual void handle_signal(int signal)
00199   {
00200     __quit = true;
00201     __fam->interrupt();
00202   }
00203 
00204   void run()
00205   {
00206     while (! __quit) {
00207       __fam->process_events(-1);
00208     }
00209   }
00210 
00211 
00212  private:
00213   bool __quit;
00214   FileAlterationMonitor *__fam;
00215   BBLogFile &__file;
00216 };
00217 
00218 int
00219 watch_file(std::string &filename)
00220 {
00221   BBLogFile file(filename.c_str(), NULL, false);
00222   if (file.remaining_entries() > 0) {
00223     // jump to end of file
00224     file.read_index(file.remaining_entries() - 1);
00225   }
00226   BBLogWatcher watcher(filename.c_str(), file);
00227   watcher.run();
00228 
00229   return 0;  
00230 }
00231 
00232 
00233 int
00234 set_enabled(const char *hostname, unsigned short int port, bool enabled)
00235 {
00236   bool rv = 0;
00237 
00238   BlackBoard *bb = new RemoteBlackBoard(hostname, port);
00239   SwitchInterface *si = bb->open_for_reading<SwitchInterface>("BBLogger");
00240   if ( ! si->has_writer() ) {
00241     printf("No writer exists, BBLogger not loaded?\n");
00242     rv = -1;
00243   } else {
00244     if (enabled) {
00245       si->msgq_enqueue(new SwitchInterface::EnableSwitchMessage());
00246     } else {
00247       si->msgq_enqueue(new SwitchInterface::DisableSwitchMessage());
00248     }
00249   }
00250 
00251   bb->close(si);
00252   delete bb;
00253   return rv;
00254 }
00255 
00256 /// @endcond
00257 
00258 void
00259 convert_file_csv(BBLogFile &bf, FILE *outf)
00260 {
00261   fawkes::Interface *iface = bf.interface();
00262 
00263   // print header row
00264   fprintf(outf, "# Time relative to beginning (in sec)");
00265   InterfaceFieldIterator i;
00266   for (i = iface->fields(); i != iface->fields_end(); ++i) {
00267     fprintf(outf, ";%s (%s[%zu])",
00268             i.get_name(), i.get_typename(), i.get_length());
00269   }
00270   fprintf(outf, "\n");
00271 
00272   while (bf.has_next()) {
00273     bf.read_next();
00274     fprintf(outf, "%f", bf.entry_offset().in_sec());
00275 
00276     InterfaceFieldIterator i;
00277     for (i = iface->fields(); i != iface->fields_end(); ++i) {
00278       fprintf(outf, ";%s", i.get_value_string());
00279     }
00280     fprintf(outf, "\n");
00281   }
00282 }
00283 
00284 
00285 int
00286 convert_file(std::string &infile, std::string &outfile, std::string &format)
00287 {
00288   if (format != "csv") {
00289     printf("Unsupported output format '%s'\n", format.c_str());
00290     return 8;
00291   }
00292 
00293   FILE *outf = fopen(outfile.c_str(), "wx");
00294   if (!outf) {
00295     perror("Failed to open output file");
00296     return 3;
00297   }
00298 
00299   try {
00300     BBLogFile bf(infile.c_str());
00301 
00302     // Do the conversion!
00303     if (format == "csv") {
00304       convert_file_csv(bf, outf);
00305     }
00306 
00307   } catch (Exception &e) {
00308     printf("Failed to convert log file: %s\n", e.what());
00309     e.print_trace();
00310     fclose(outf);
00311     return 4;
00312   }
00313 
00314   fclose(outf);
00315 
00316   return 0;
00317 }
00318 
00319 /** BBLogger tool main.
00320  * @param argc argument count
00321  * @param argv arguments
00322  */
00323 int
00324 main(int argc, char **argv)
00325 {
00326   ArgumentParser argp(argc, argv, "h");
00327 
00328   if ( argp.has_arg("h") ) {
00329     print_usage(argv[0]);
00330     exit(0);
00331   }
00332 
00333   std::string command, file;
00334   if (argp.num_items() < 1) {
00335     printf("Invalid number of arguments\n");
00336     print_usage(argv[0]);
00337     exit(1);
00338   } else if (argp.num_items() >= 2) {
00339     file    = argp.items()[1];
00340   }
00341 
00342   command = argp.items()[0];
00343 
00344   if (command == "watch") {
00345     return watch_file(file);
00346 
00347   } else if (command == "info") {
00348     return print_info(file);
00349 
00350   } else if (command == "print") {
00351     std::vector<const char *> index_strings = argp.items();
00352     index_strings.erase(index_strings.begin(), index_strings.begin() + 2);
00353 
00354     std::vector<unsigned int> indexes(index_strings.size());
00355     for (unsigned int i = 0; i < index_strings.size(); ++i) {
00356       long l = atol(index_strings[i]);
00357       if (l < 0) throw Exception("Invalid index %li", l);
00358 
00359       indexes[i] = l;
00360     }
00361 
00362     if (indexes.size() == 0) {
00363       printf("No indexes given.\n\n");
00364       print_usage(argv[0]);
00365       exit(6);
00366     }
00367 
00368     return print_indexes(file, indexes);
00369 
00370   } else if (command == "replay") {
00371     return replay_file(file);
00372 
00373   } else if (command == "repair") {
00374     return repair_file(file);
00375 
00376   } else if ( (command == "enable") || (command == "disable")) {
00377     char *host = strdup("localhost");
00378     unsigned short int port = 1910;
00379     if (argp.has_arg("r")) {
00380       argp.parse_hostport("r", &host, &port);
00381     }
00382     int rv = set_enabled(host, port, (command == "enable"));
00383     free(host);
00384     return rv;
00385 
00386   } else if (command == "convert") {
00387     if (argp.num_items() != 4) {
00388       printf("Invalid number of arguments\n");
00389       print_usage(argv[0]);
00390       exit(7);
00391     }
00392     std::string outfile = argp.items()[2];
00393     std::string format = argp.items()[3];
00394     return convert_file(file, outfile, format);
00395 
00396   } else {
00397     printf("Invalid command '%s'\n", command.c_str());
00398     print_usage(argv[0]);
00399     exit(2);
00400   }
00401 
00402   return 0;
00403 }