00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "bblogfile.h"
00024
00025 #include <core/exceptions/system.h>
00026 #include <blackboard/internal/instance_factory.h>
00027
00028 #include <cstdlib>
00029 #include <cerrno>
00030 #include <cstring>
00031 #ifdef __FreeBSD__
00032 # include <sys/endian.h>
00033 #else
00034 # include <endian.h>
00035 #endif
00036 #include <arpa/inet.h>
00037 #include <sys/types.h>
00038 #include <sys/stat.h>
00039 #include <unistd.h>
00040 #include <sys/mman.h>
00041
00042 using namespace fawkes;
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062 BBLogFile::BBLogFile(const char *filename, fawkes::Interface *interface,
00063 bool do_sanity_check)
00064 {
00065 ctor(filename, do_sanity_check);
00066
00067 if (interface) {
00068 __instance_factory = NULL;
00069 __interface = interface;
00070 if ((strcmp(__interface->type(), __interface_type) != 0) ||
00071 (strcmp(__interface->id(), __interface_id) != 0)) {
00072 fclose(__f);
00073 free(__filename);
00074 free(__scenario);
00075 free(__interface_type);
00076 free(__interface_id);
00077 throw Exception("Interface UID %s does not match expected %s:%s",
00078 __interface->uid(), __interface_type, __interface_id);
00079 }
00080 } else {
00081 __instance_factory = new BlackBoardInstanceFactory();
00082 __interface = __instance_factory->new_interface_instance(__interface_type,
00083 __interface_id);
00084 }
00085 }
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098 BBLogFile::BBLogFile(const char *filename, bool do_sanity_check)
00099 {
00100 ctor(filename, do_sanity_check);
00101
00102 __instance_factory = NULL;
00103 __interface = NULL;
00104 }
00105
00106
00107 void
00108 BBLogFile::ctor(const char *filename, bool do_sanity_check)
00109 {
00110 __f = fopen(filename, "r");
00111 if (!__f) {
00112 throw CouldNotOpenFileException(filename, errno);
00113 }
00114
00115 __filename = strdup(filename);
00116 __header = (bblog_file_header *)malloc(sizeof(bblog_file_header));
00117
00118 try {
00119 read_file_header();
00120 if (do_sanity_check) sanity_check();
00121 } catch (Exception &e) {
00122 free(__filename);
00123 free(__scenario);
00124 free(__interface_type);
00125 free(__interface_id);
00126 fclose(__f);
00127 throw;
00128 }
00129
00130 __ifdata = malloc(__header->data_size);
00131 }
00132
00133
00134 BBLogFile::~BBLogFile()
00135 {
00136 if (__instance_factory) {
00137 __instance_factory->delete_interface_instance(__interface);
00138 delete __instance_factory;
00139 }
00140
00141 fclose(__f);
00142
00143 free(__filename);
00144 free(__scenario);
00145 free(__interface_type);
00146 free(__interface_id);
00147
00148 free(__header);
00149 free(__ifdata);
00150 }
00151
00152
00153
00154 void
00155 BBLogFile::read_file_header()
00156 {
00157 uint32_t magic;
00158 uint32_t version;
00159 if ((fread(&magic, sizeof(uint32_t), 1, __f) == 1) &&
00160 (fread(&version, sizeof(uint32_t), 1, __f) == 1) ) {
00161 if ( (ntohl(magic) == BBLOGGER_FILE_MAGIC) &&
00162 (ntohl(version) == BBLOGGER_FILE_VERSION) ) {
00163 ::rewind(__f);
00164 if (fread(__header, sizeof(bblog_file_header), 1, __f) != 1) {
00165 throw FileReadException(__filename, errno, "Failed to read file header");
00166 }
00167 } else {
00168 throw Exception("File magic/version %X/%u does not match (expected %X/%u)",
00169 ntohl(magic), ntohl(version),
00170 BBLOGGER_FILE_VERSION, BBLOGGER_FILE_MAGIC);
00171 }
00172 } else {
00173 throw Exception(__filename, errno, "Failed to read magic/version from file");
00174 }
00175
00176 __scenario = strndup(__header->scenario, BBLOG_SCENARIO_SIZE);
00177 __interface_type = strndup(__header->interface_type, BBLOG_INTERFACE_TYPE_SIZE);
00178 __interface_id = strndup(__header->interface_id, BBLOG_INTERFACE_ID_SIZE);
00179
00180 __start_time.set_time(__header->start_time_sec, __header->start_time_usec);
00181 }
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191 void
00192 BBLogFile::sanity_check()
00193 {
00194 if (__header->num_data_items == 0) {
00195 Exception e("File %s does not specify number of data items", __filename);
00196 e.set_type_id("bblogfile-num-items-zero");
00197 throw e;
00198 }
00199
00200 struct stat fs;
00201 if (fstat(fileno(__f), &fs) != 0) {
00202 Exception e(errno, "Failed to stat file %s", __filename);
00203 e.set_type_id("bblogfile-stat-failed");
00204 throw e;
00205 }
00206
00207 long int expected_size = sizeof(bblog_file_header)
00208 + __header->num_data_items * __header->data_size
00209 + __header->num_data_items * sizeof(bblog_entry_header);
00210 if (expected_size != fs.st_size) {
00211 Exception e("Size of file %s does not match expectation "
00212 "(actual: %li, actual: %li)",
00213 __filename, expected_size, (long int)fs.st_size);
00214 e.set_type_id("bblogfile-file-size-mismatch");
00215 throw e;
00216 }
00217
00218 #if __BYTE_ORDER == __LITTLE_ENDIAN
00219 if (__header->endianess == 1)
00220 #else
00221 if (__header->endianess == 0)
00222 #endif
00223 {
00224 Exception e("File %s has incompatible endianess", __filename);
00225 e.set_type_id("bblogfile-endianess-mismatch");
00226 throw e;
00227 }
00228 }
00229
00230
00231
00232
00233 void
00234 BBLogFile::read_index(unsigned int index)
00235 {
00236 long offset = sizeof(bblog_file_header)
00237 + (sizeof(bblog_entry_header) + __header->data_size) * index;
00238
00239 if (fseek(__f, offset, SEEK_SET) != 0) {
00240 throw Exception(errno, "Cannot seek to index %u", index);
00241 }
00242
00243 read_next();
00244 }
00245
00246
00247
00248
00249
00250 void
00251 BBLogFile::rewind()
00252 {
00253 if (fseek(__f, sizeof(bblog_file_header), SEEK_SET) != 0) {
00254 throw Exception(errno, "Cannot reset file");
00255 }
00256 __entry_offset.set_time(0, 0);
00257 }
00258
00259
00260
00261
00262
00263 bool
00264 BBLogFile::has_next()
00265 {
00266
00267 clearerr(__f);
00268 if (getc(__f) == EOF) {
00269 return false;
00270 } else {
00271 fseek(__f, -1, SEEK_CUR);
00272 return true;
00273 }
00274 }
00275
00276
00277
00278
00279
00280 void
00281 BBLogFile::read_next()
00282 {
00283 bblog_entry_header entryh;
00284
00285 if ( (fread(&entryh, sizeof(bblog_entry_header), 1, __f) == 1) &&
00286 (fread(__ifdata, __header->data_size, 1, __f) == 1) ) {
00287 __entry_offset.set_time(entryh.rel_time_sec, entryh.rel_time_usec);
00288 __interface->set_from_chunk(__ifdata);
00289 } else {
00290 throw Exception("Cannot read interface data");
00291 }
00292 }
00293
00294
00295
00296
00297
00298
00299
00300 void
00301 BBLogFile::set_num_entries(size_t num_entries)
00302 {
00303 #if _POSIX_MAPPED_FILES
00304 void *h = mmap(NULL, sizeof(bblog_file_header), PROT_WRITE, MAP_SHARED,
00305 fileno(__f), 0);
00306 if (h == MAP_FAILED) {
00307 throw Exception(errno, "Failed to mmap log, not updating number of data items");
00308 } else {
00309 bblog_file_header *header = (bblog_file_header *)h;
00310 header->num_data_items = num_entries;
00311 munmap(h, sizeof(bblog_file_header));
00312 }
00313 #else
00314 throw Exception("Cannot set number of entries, mmap not available.");
00315 #endif
00316 }
00317
00318
00319
00320
00321
00322
00323 void
00324 BBLogFile::repair_file(const char *filename)
00325 {
00326 BBLogFile file(filename, NULL, false);
00327 file.repair();
00328 }
00329
00330
00331
00332
00333
00334
00335 void
00336 BBLogFile::repair()
00337 {
00338 FILE *f = freopen(__filename, "r+", __f);
00339 if (! f) {
00340 throw Exception("Reopening file %s with new mode failed", __filename);
00341 }
00342 __f = f;
00343
00344 bool repair_done = false;
00345
00346 Exception success("Successfully repaired file");
00347 success.set_type_id("repair-success");
00348
00349 #if __BYTE_ORDER == __LITTLE_ENDIAN
00350 if (__header->endianess == 1)
00351 #else
00352 if (__header->endianess == 0)
00353 #endif
00354 {
00355 throw Exception("File %s has incompatible endianess. Cannot repair.",
00356 __filename);
00357 }
00358
00359 struct stat fs;
00360 if (fstat(fileno(__f), &fs) != 0) {
00361 throw Exception(errno, "Failed to stat file %s", __filename);
00362 }
00363
00364 size_t entry_size = sizeof(bblog_entry_header) + __header->data_size;
00365 size_t all_entries_size = fs.st_size - sizeof(bblog_file_header);
00366 size_t num_entries = all_entries_size / entry_size;
00367 size_t extra_bytes = all_entries_size % entry_size;
00368
00369 if (extra_bytes != 0) {
00370 success.append("FIXING: errorneous bytes at end of file, "
00371 "truncating by %zu b", extra_bytes);
00372 if (ftruncate(fileno(__f), fs.st_size - extra_bytes) == -1) {
00373 throw Exception(errno, "Failed to truncate file %s", __filename);
00374 }
00375 all_entries_size -= extra_bytes;
00376 extra_bytes = 0;
00377 if (fstat(fileno(__f), &fs) != 0) {
00378 throw Exception(errno, "Failed to update information of file %s "
00379 "after truncate", __filename);
00380 }
00381 repair_done = true;
00382 }
00383 if (__header->num_data_items == 0) {
00384 success.append("FIXING: header of file %s has 0 data items, setting to %zu.",
00385 __filename, num_entries);
00386 set_num_entries(num_entries);
00387 repair_done = true;
00388 } else if (__header->num_data_items != num_entries) {
00389 success.append("FIXING: header has %u data items, but expecting %zu, setting",
00390 __header->num_data_items, num_entries);
00391 set_num_entries(num_entries);
00392 repair_done = true;
00393 }
00394
00395 f = freopen(__filename, "r", __f);
00396 if (! f) {
00397 throw Exception("Reopening file %s with read-only mode failed", __filename);
00398 }
00399 __f = f;
00400
00401 if (repair_done) {
00402 throw success;
00403 }
00404 }
00405
00406
00407
00408
00409
00410
00411 void
00412 BBLogFile::print_info(const char *line_prefix, FILE *outf)
00413 {
00414 char interface_hash[BBLOG_INTERFACE_HASH_SIZE * 2 + 1];
00415
00416 for (unsigned int i = 0; i < BBLOG_INTERFACE_HASH_SIZE; ++i) {
00417 snprintf(&interface_hash[i*2], 3, "%02X", __header->interface_hash[i]);
00418 }
00419
00420 struct stat fs;
00421 if (fstat(fileno(__f), &fs) != 0) {
00422 throw Exception(errno, "Failed to get stat file");
00423 }
00424
00425 fprintf(outf,
00426 "%sFile version: %-10u Endianess: %s Endian\n"
00427 "%s# data items: %-10u Data size: %u bytes\n"
00428 "%sHeader size: %zu bytes File size: %li bytes\n"
00429 "%s\n"
00430 "%sScenario: %s\n"
00431 "%sInterface: %s::%s (%s)\n"
00432 "%sStart time: %s\n",
00433 line_prefix, ntohl(__header->file_version),
00434 (__header->endianess == 1) ? "Big" : "Little",
00435 line_prefix, __header->num_data_items, __header->data_size,
00436 line_prefix, sizeof(bblog_file_header), (long int)fs.st_size,
00437 line_prefix,
00438 line_prefix, __scenario,
00439 line_prefix, __interface_type, __interface_id, interface_hash,
00440 line_prefix, __start_time.str());
00441
00442 }
00443
00444
00445
00446
00447
00448
00449 void
00450 BBLogFile::print_entry(FILE *outf)
00451 {
00452 fprintf(outf, "Time Offset: %f\n", __entry_offset.in_sec());
00453
00454 InterfaceFieldIterator i;
00455 for (i = __interface->fields(); i != __interface->fields_end(); ++i) {
00456 char *typesize;
00457 if (i.get_length() > 1) {
00458 if (asprintf(&typesize, "%s[%zu]", i.get_typename(), i.get_length()) == -1) {
00459 throw Exception("Out of memory");
00460 }
00461 } else {
00462 if (asprintf(&typesize, "%s", i.get_typename()) == -1) {
00463 throw Exception("Out of memory");
00464 }
00465 }
00466 fprintf(outf, "%-16s %-18s: %s\n",
00467 i.get_name(), typesize, i.get_value_string());
00468 free(typesize);
00469 }
00470 }
00471
00472
00473
00474
00475
00476 fawkes::Interface *
00477 BBLogFile::interface()
00478 {
00479 return __interface;
00480 }
00481
00482
00483
00484
00485
00486
00487 void
00488 BBLogFile::set_interface(fawkes::Interface *interface)
00489 {
00490 if ( (strcmp(interface->type(), __interface_type) == 0) &&
00491 (strcmp(interface->id(), __interface_id) == 0) &&
00492 (memcmp(interface->hash(), __header->interface_hash,
00493 __INTERFACE_HASH_SIZE) == 0) ) {
00494 if (__instance_factory) {
00495 __instance_factory->delete_interface_instance(__interface);
00496 delete __instance_factory;
00497 __instance_factory = NULL;
00498 }
00499 __interface = interface;
00500 } else {
00501 throw TypeMismatchException("Interfaces incompatible");
00502 }
00503 }
00504
00505
00506
00507
00508
00509
00510 const fawkes::Time &
00511 BBLogFile::entry_offset() const
00512 {
00513 return __entry_offset;
00514 }
00515
00516
00517
00518
00519
00520 uint32_t
00521 BBLogFile::file_version() const
00522 {
00523 return ntohl(__header->file_version);
00524 }
00525
00526
00527
00528
00529
00530 bool
00531 BBLogFile::is_big_endian() const
00532 {
00533 return (__header->endianess == 1);
00534 }
00535
00536
00537
00538
00539 uint32_t
00540 BBLogFile::num_data_items() const
00541 {
00542 return __header->num_data_items;
00543 }
00544
00545
00546
00547
00548
00549 const char *
00550 BBLogFile::scenario() const
00551 {
00552 return __scenario;
00553 }
00554
00555
00556
00557
00558
00559 const char *
00560 BBLogFile::interface_type() const
00561 {
00562 return __interface_type;
00563 }
00564
00565
00566
00567
00568
00569 const char *
00570 BBLogFile::interface_id() const
00571 {
00572 return __interface_id;
00573 }
00574
00575
00576
00577
00578
00579
00580 unsigned char *
00581 BBLogFile::interface_hash() const
00582 {
00583 return __header->interface_hash;
00584 }
00585
00586
00587
00588
00589
00590 uint32_t
00591 BBLogFile::data_size()
00592 {
00593 return __header->data_size;
00594 }
00595
00596
00597
00598
00599
00600 fawkes::Time &
00601 BBLogFile::start_time()
00602 {
00603 return __start_time;
00604 }
00605
00606
00607
00608
00609
00610 unsigned int
00611 BBLogFile::remaining_entries()
00612 {
00613
00614 size_t entry_size = sizeof(bblog_entry_header) + __header->data_size;
00615 long curpos = ftell(__f);
00616 size_t fsize = file_size();
00617 size_t sizediff = fsize - curpos;
00618
00619 if (sizediff < 0) {
00620 throw Exception("File %s shrank while reading it", __filename);
00621 }
00622
00623 return sizediff / entry_size;
00624 }
00625
00626
00627
00628
00629 size_t
00630 BBLogFile::file_size() const
00631 {
00632 struct stat fs;
00633 if (fstat(fileno(__f), &fs) != 0) {
00634 Exception e(errno, "Failed to stat file %s", __filename);
00635 e.set_type_id("bblogfile-stat-failed");
00636 throw e;
00637 }
00638 return fs.st_size;
00639 }