00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <barry/barry.h>
00023 #include <barry/barrybackup.h>
00024
00025 #include <iostream>
00026 #include <iomanip>
00027 #include <tr1/memory>
00028 #include <string>
00029 #include <vector>
00030 #include <map>
00031 #include <algorithm>
00032 #include <stdexcept>
00033
00034 #include "barrygetopt.h"
00035 #include "util.h"
00036
00037 using namespace std;
00038 using namespace std::tr1;
00039 using namespace Barry;
00040
00041
00042
00043
00044
00045
00046
00047 void Usage()
00048 {
00049 int logical, major, minor;
00050 const char *Version = Barry::Version(logical, major, minor);
00051
00052 cerr
00053 << "btarcmp - Compare Barry backup tarballs\n"
00054 << " Copyright 2012, Net Direct Inc. (http://www.netdirect.ca/)\n"
00055 << " Using: " << Version << "\n"
00056 << "\n"
00057 << " Usage: btarcmp [options...] tarball_0 tarball_1\n"
00058 << "\n"
00059 << " -b Use brief filename output\n"
00060 << " -d db Specify a specific database to compare. Can be used\n"
00061 << " multiple times. If not used at all, all databases are\n"
00062 << " compared.\n"
00063 << " -D db Specify a database name to skip. If both -d and -D are\n"
00064 << " used for the same database name, it will be skipped.\n"
00065 << " -h This help\n"
00066 << " -I cs International charset for string conversions\n"
00067 << " Valid values here are available with 'iconv --list'\n"
00068 << " -P Only compare records that can be parsed\n"
00069 << " This is the same as specifying -d for each database\n"
00070 << " listed with -S.\n"
00071 << " -S Show list of supported database parsers. Use twice\n"
00072 << " to show field names as well.\n"
00073 << " -v Show verbose diff output (twice to force hex output)\n"
00074 << "\n"
00075 << endl;
00076 }
00077
00078
00079
00080
00081
00082 bool DBDataCmp(const DBData &a, const DBData &b)
00083 {
00084 return a.GetUniqueId() < b.GetUniqueId();
00085 }
00086
00087 bool UnknownCmp(const UnknownField &a, const UnknownField &b)
00088 {
00089 return a.type < b.type;
00090 }
00091
00092 class DBDataIdCmp
00093 {
00094 uint32_t m_id;
00095
00096 public:
00097 explicit DBDataIdCmp(uint32_t id)
00098 : m_id(id)
00099 {
00100 }
00101
00102 bool operator()(const DBData &data) const
00103 {
00104 return data.GetUniqueId() == m_id;
00105 }
00106 };
00107
00108 void ChecksumDBData(const DBData &data, bool include_ids, std::string &sum)
00109 {
00110 Barry::SHA_CTX m_ctx;
00111
00112 SHA1_Init(&m_ctx);
00113
00114 if( include_ids ) {
00115 SHA1_Update(&m_ctx, data.GetDBName().c_str(),
00116 data.GetDBName().size());
00117
00118 uint8_t recType = data.GetRecType();
00119 SHA1_Update(&m_ctx, &recType, sizeof(recType));
00120
00121 uint32_t uniqueId = data.GetUniqueId();
00122 SHA1_Update(&m_ctx, &uniqueId, sizeof(uniqueId));
00123 }
00124
00125 int len = data.GetData().GetSize() - data.GetOffset();
00126 SHA1_Update(&m_ctx,
00127 data.GetData().GetData() + data.GetOffset(), len);
00128
00129 unsigned char sha1[SHA_DIGEST_LENGTH];
00130 SHA1_Final(sha1, &m_ctx);
00131
00132 ostringstream oss;
00133 for( int i = 0; i < SHA_DIGEST_LENGTH; i++ ) {
00134 oss << hex << setfill('0') << setw(2)
00135 << (unsigned int) sha1[i];
00136 }
00137 sum = oss.str();
00138 }
00139
00140
00141
00142
00143
00144 class ParsedCompare
00145 {
00146 private:
00147 const DBData &m_one, &m_two;
00148 const IConverter *m_ic;
00149 bool m_known_record;
00150 std::string m_first_description;
00151
00152 public:
00153 ParsedCompare(const DBData &one, const DBData &two,
00154 const IConverter *ic = 0);
00155
00156 bool CanParse() const { return m_known_record; }
00157 const std::string& GetDescription() const { return m_first_description;}
00158
00159
00160
00161 bool ShowDifferingFields();
00162 };
00163
00164 ParsedCompare::ParsedCompare(const DBData &one,
00165 const DBData &two,
00166 const IConverter *ic)
00167 : m_one(one)
00168 , m_two(two)
00169 , m_ic(ic)
00170 , m_known_record(false)
00171 {
00172 #undef HANDLE_PARSER
00173 #define HANDLE_PARSER(tname) \
00174 else if( tname::GetDBName() == one.GetDBName() ) { \
00175 m_known_record = true; \
00176 tname a; \
00177 ParseDBData(m_one, a, m_ic); \
00178 m_first_description = a.GetDescription(); \
00179 }
00180
00181 if( one.GetDBName() != two.GetDBName() ) {
00182 throw logic_error("Different database types in ParsedCompare ctor!");
00183 }
00184
00185 ALL_KNOWN_PARSER_TYPES
00186 }
00187
00188 template <class RecordT>
00189 class FieldHandler
00190 {
00191 private:
00192 const RecordT &m_one, &m_two;
00193 mutable bool m_found_difference;
00194
00195 public:
00196 FieldHandler(const RecordT &one, const RecordT &two)
00197 : m_one(one)
00198 , m_two(two)
00199 , m_found_difference(false)
00200 {
00201 }
00202
00203 bool Differing() const { return m_found_difference; }
00204
00205 void operator()(EnumFieldBase<RecordT> *ep,
00206 const FieldIdentity &id) const
00207 {
00208 if( ep->GetValue(m_one) == ep->GetValue(m_two) )
00209 return;
00210
00211 m_found_difference = true;
00212 cout << " " << id.Name << ":\n"
00213 << " tar[0] = "
00214 << ep->GetName(ep->GetValue(m_one))
00215 << " (" << ep->GetValue(m_one) << ")\n"
00216 << " tar[1] = "
00217 << ep->GetName(ep->GetValue(m_two))
00218 << " (" << ep->GetValue(m_two) << ")"
00219 << endl;
00220 }
00221
00222 void operator()(typename FieldHandle<RecordT>::PostalPointer pp,
00223 const FieldIdentity &id) const
00224 {
00225 const std::string
00226 &a = m_one.*(pp.m_PostalAddress).*(pp.m_PostalField),
00227 &b = m_two.*(pp.m_PostalAddress).*(pp.m_PostalField);
00228
00229 if( a == b )
00230 return;
00231
00232 m_found_difference = true;
00233 cout << " " << id.Name << ":\n"
00234 << " tar[0] = '" << a << "'\n"
00235 << " tar[1] = '" << b << "'"
00236 << endl;
00237 }
00238
00239 void operator()(std::string RecordT::* mp, const FieldIdentity &id) const
00240 {
00241 if( m_one.*mp == m_two.*mp )
00242 return;
00243
00244 m_found_difference = true;
00245 cout << " " << id.Name << ":\n"
00246 << " tar[0] = '"
00247 << Cr2LfWrapper(m_one.*mp) << "'\n"
00248 << " tar[1] = '"
00249 << Cr2LfWrapper(m_two.*mp) << "'"
00250 << endl;
00251 }
00252
00253 void operator()(UnknownsType RecordT::* mp, const FieldIdentity &id) const
00254 {
00255 UnknownsType a = m_one.*mp, b = m_two.*mp;
00256
00257 sort(a.begin(), a.end(), UnknownCmp);
00258 sort(b.begin(), b.end(), UnknownCmp);
00259
00260 if( a == b )
00261 return;
00262
00263 m_found_difference = true;
00264 cout << " " << id.Name << ":\n"
00265 << " tar[0] = '" << a << "'\n"
00266 << " tar[1] = '" << b << "'"
00267 << endl;
00268 }
00269
00270 template <class TypeT>
00271 void operator()(TypeT RecordT::* mp, const FieldIdentity &id) const
00272 {
00273 if( m_one.*mp == m_two.*mp )
00274 return;
00275
00276 m_found_difference = true;
00277 cout << " " << id.Name << ":\n"
00278 << " tar[0] = '" << m_one.*mp << "'\n"
00279 << " tar[1] = '" << m_two.*mp << "'"
00280 << endl;
00281 }
00282 };
00283
00284 template <class RecordT>
00285 bool DoParsedCompare(const RecordT &a, const RecordT &b)
00286 {
00287 FieldHandler<RecordT> handler(a, b);
00288 ForEachField(RecordT::GetFieldHandles(), handler);
00289 return handler.Differing();
00290 }
00291
00292
00293
00294 bool ParsedCompare::ShowDifferingFields()
00295 {
00296 #undef HANDLE_PARSER
00297 #define HANDLE_PARSER(tname) \
00298 else if( tname::GetDBName() == m_one.GetDBName() ) { \
00299 tname a, b; \
00300 ParseDBData(m_one, a, m_ic); \
00301 ParseDBData(m_two, b, m_ic); \
00302 return DoParsedCompare<tname>(a, b); \
00303 }
00304
00305 if( !m_known_record ) {
00306 return false;
00307 }
00308
00309 ALL_KNOWN_PARSER_TYPES
00310
00311 else {
00312 return false;
00313 }
00314 }
00315
00316
00317
00318
00319
00320 class App
00321 {
00322 public:
00323 typedef Barry::ConfigFile::DBListType DBListType;
00324 typedef std::vector<Barry::DBData> DBDataList;
00325 typedef std::map<std::string, DBDataList> DatabaseMap;
00326
00327 private:
00328 DBListType m_compare_list;
00329 DBListType m_skip_list;
00330 DBListType m_valid_list;
00331
00332
00333
00334 DatabaseMap m_tars[2];
00335 std::string m_tarpaths[2];
00336 std::string m_tarfiles[2];
00337 auto_ptr<IConverter> m_ic;
00338
00339 int m_main_return;
00340
00341
00342
00343 bool m_verbose;
00344 bool m_always_hex;
00345 bool m_sort_on_load;
00346
00347 bool m_include_ids;
00348
00349 std::string m_last_dbname;
00350
00351 public:
00352 App();
00353
00354 void LoadTarballs();
00355 void CompareDatabaseNames();
00356 void CompareData();
00357 void Compare(const std::string &dbname);
00358 void Compare(const std::string &dbname,
00359 const DBDataList &one, const DBDataList &two);
00360 void Compare(const DBData &one, const DBData &two);
00361
00362 bool Alike(DBDataList::const_iterator b1, DBDataList::const_iterator b2,
00363 DBDataList::const_iterator e1, DBDataList::const_iterator e2);
00364 void SearchCheck(DBDataList::const_iterator &b,
00365 DBDataList::const_iterator &e, const DBDataList &opposite_list,
00366 const std::string &action);
00367
00368 void ShowRecordDiff(const DBData &one, const DBData &two,
00369 ParsedCompare &pc);
00370 void DumpRecord(const DBData &data);
00371 void ShowDatabaseHeader(const std::string &dbname);
00372 void AddParsersToCompare();
00373
00374
00375 int main(int argc, char *argv[]);
00376 };
00377
00378
00379
00380
00381 class StoreParser : public Barry::Parser
00382 {
00383 App::DatabaseMap &m_map;
00384
00385 public:
00386 explicit StoreParser(App::DatabaseMap &map)
00387 : m_map(map)
00388 {
00389 }
00390
00391 virtual void ParseRecord(const DBData &data, const IConverter *ic)
00392 {
00393 m_map[data.GetDBName()].push_back(data);
00394 }
00395 };
00396
00397
00398
00399
00400
00401 bool IdExists(const App::DBDataList &list, uint32_t id)
00402 {
00403 return find_if(list.begin(), list.end(), DBDataIdCmp(id)) != list.end();
00404 }
00405
00406
00407
00408
00409 App::App()
00410 : m_main_return(0)
00411 , m_verbose(false)
00412 , m_always_hex(false)
00413 , m_sort_on_load(true)
00414 , m_include_ids(true)
00415 {
00416 }
00417
00418 void App::AddParsersToCompare()
00419 {
00420 #undef HANDLE_PARSER
00421 #define HANDLE_PARSER(tname) \
00422 m_compare_list.push_back(tname::GetDBName());
00423
00424 ALL_KNOWN_PARSER_TYPES
00425 }
00426
00427 void App::LoadTarballs()
00428 {
00429 for( int i = 0; i < 2; i++ ) {
00430
00431 Restore builder(m_tarpaths[i]);
00432 StoreParser parser(m_tars[i]);
00433
00434 Pipe pipe(builder);
00435 pipe.PumpFile(parser, m_ic.get());
00436
00437
00438 for( DatabaseMap::iterator b = m_tars[i].begin();
00439 b != m_tars[i].end();
00440 ++b )
00441 {
00442 if( m_sort_on_load )
00443 sort(b->second.begin(), b->second.end(), DBDataCmp);
00444 }
00445 }
00446 }
00447
00448 void App::CompareDatabaseNames()
00449 {
00450 for( int i = 1; i >= 0; i-- ) {
00451 int other = i == 0 ? 1 : 0;
00452
00453 DatabaseMap::const_iterator b = m_tars[i].begin(), match;
00454 for( ; b != m_tars[i].end(); ++b ) {
00455 match = m_tars[other].find(b->first);
00456 if( match == m_tars[other].end() ) {
00457 cout << m_tarfiles[other] << ": has no database '" << b->first << "'" << endl;
00458 m_main_return = 2;
00459 }
00460 else {
00461 if( !m_valid_list.IsSelected(b->first) ) {
00462 m_valid_list.push_back(b->first);
00463 }
00464 }
00465 }
00466 }
00467
00468
00469 sort(m_valid_list.begin(), m_valid_list.end());
00470
00471 }
00472
00473 void App::CompareData()
00474 {
00475 DBListType::const_iterator valid = m_valid_list.begin();
00476 for( ; valid != m_valid_list.end(); ++valid ) {
00477
00478
00479 if( m_compare_list.size() && !m_compare_list.IsSelected(*valid) )
00480 continue;
00481
00482
00483 if( m_skip_list.IsSelected(*valid) )
00484 continue;
00485
00486
00487 Compare(*valid);
00488 }
00489 }
00490
00491 void App::Compare(const std::string &dbname)
00492 {
00493 DatabaseMap::const_iterator tar[2];
00494 tar[0] = m_tars[0].find(dbname);
00495 tar[1] = m_tars[1].find(dbname);
00496
00497 if( tar[0] == m_tars[0].end() || tar[1] == m_tars[1].end() )
00498 throw logic_error("Comparing non-existant database!" + dbname);
00499
00500 Compare(dbname, tar[0]->second, tar[1]->second);
00501 }
00502
00503 void App::Compare(const std::string &dbname,
00504 const DBDataList &one,
00505 const DBDataList &two)
00506 {
00507 DBDataList::const_iterator
00508 b1 = one.begin(), e1 = one.end(),
00509 b2 = two.begin(), e2 = two.end(),
00510 s1, s2;
00511
00512
00513
00514
00515
00516
00517
00518
00519 while( b1 != e1 || b2 != e2 ) {
00520 if( Alike(b1, b2, e1, e2 ) ) {
00521 Compare(*b1, *b2);
00522 ++b1;
00523 ++b2;
00524 continue;
00525 }
00526 else {
00527
00528 SearchCheck(b1, e1, two, "deleted");
00529 SearchCheck(b2, e2, one, "added");
00530 }
00531 }
00532 }
00533
00534 void App::Compare(const DBData &one, const DBData &two)
00535 {
00536
00537 if( one.GetDBName() != two.GetDBName() )
00538 throw logic_error("Tried to compare records from different databases: " + one.GetDBName() + ", and " + two.GetDBName());
00539
00540
00541 string sum1, sum2;
00542 ChecksumDBData(one, m_include_ids, sum1);
00543 ChecksumDBData(two, m_include_ids, sum2);
00544 if( sum1 == sum2 )
00545 return;
00546
00547
00548 ShowDatabaseHeader(one.GetDBName());
00549
00550
00551
00552
00553 ParsedCompare pc(one, two, m_ic.get());
00554 ShowRecordDiff(one, two, pc);
00555 }
00556
00557 void App::ShowRecordDiff(const DBData &one,
00558 const DBData &two,
00559 ParsedCompare &pc)
00560 {
00561 if( !pc.CanParse() ) {
00562
00563
00564
00565 cout << " 0x" << hex << one.GetUniqueId() << ": differs: "
00566 << dec
00567 << "sizes (" << one.GetData().GetSize()
00568 << " vs. " << two.GetData().GetSize()
00569 << "), SHA1 sums differ"
00570 << endl;
00571 }
00572 else {
00573
00574
00575 cout << " 0x" << hex << one.GetUniqueId() << ": differs: "
00576 << dec
00577 << "sizes (" << one.GetData().GetSize()
00578 << " vs. " << two.GetData().GetSize()
00579 << "), "
00580 << pc.GetDescription()
00581 << endl;
00582
00583 if( !pc.ShowDifferingFields() ) {
00584
00585 cout << "No differences found in parsed records, but SHA1 sums differ." << endl;
00586 }
00587 }
00588
00589
00590
00591 if( (m_verbose && !pc.CanParse()) || m_always_hex ) {
00592 cout << " Hex diff of record:" << endl;
00593 cout << Diff(one.GetData(), two.GetData()) << endl;
00594 }
00595 }
00596
00597 bool App::Alike(DBDataList::const_iterator b1,
00598 DBDataList::const_iterator b2,
00599 DBDataList::const_iterator e1,
00600 DBDataList::const_iterator e2)
00601 {
00602 if( b1 == e1 || b2 == e2 )
00603 return false;
00604 return b1->GetUniqueId() == b2->GetUniqueId();
00605 }
00606
00607 std::string GetDBDescription(const DBData &data, const IConverter *ic)
00608 {
00609 string desc;
00610
00611
00612 #undef HANDLE_PARSER
00613 #define HANDLE_PARSER(tname) \
00614 if( data.GetDBName() == tname::GetDBName() ) { \
00615 tname rec; \
00616 ParseDBData(data, rec, ic); \
00617 return rec.GetDescription(); \
00618 }
00619
00620 ALL_KNOWN_PARSER_TYPES
00621
00622 return desc;
00623 }
00624
00625 void App::SearchCheck(DBDataList::const_iterator &b,
00626 DBDataList::const_iterator &e,
00627 const DBDataList &opposite_list,
00628 const std::string &action)
00629 {
00630
00631 if( b == e )
00632 return;
00633
00634
00635
00636 if( IdExists(opposite_list, b->GetUniqueId()) )
00637 return;
00638
00639
00640 m_main_return = 3;
00641
00642
00643
00644
00645 ShowDatabaseHeader(b->GetDBName());
00646 cout << " 0x" << hex << b->GetUniqueId() << ": record has been "
00647 << action << " in " << "tar[1]";
00648 string desc = GetDBDescription(*b, m_ic.get());
00649 if( desc.size() ) {
00650 cout << ": " << desc << endl;
00651 }
00652 else {
00653 cout << endl;
00654 }
00655 if( m_verbose ) {
00656 DumpRecord(*b);
00657 }
00658
00659
00660 ++b;
00661 }
00662
00663 void App::DumpRecord(const DBData &data)
00664 {
00665 #undef HANDLE_PARSER
00666 #define HANDLE_PARSER(tname) \
00667 if( data.GetDBName() == tname::GetDBName() ) { \
00668 tname rec; \
00669 ParseDBData(data, rec, m_ic.get()); \
00670 cout << rec << endl; \
00671 return; \
00672 }
00673
00674 ALL_KNOWN_PARSER_TYPES
00675
00676
00677 cout << data.GetData() << endl;
00678 }
00679
00680 void App::ShowDatabaseHeader(const std::string &dbname)
00681 {
00682 if( dbname != m_last_dbname ) {
00683 m_last_dbname = dbname;
00684 cout << "In database: " << dbname << endl;
00685
00686 }
00687 }
00688
00689 int App::main(int argc, char *argv[])
00690 {
00691 bool brief = false;
00692 bool show_parsers = false, show_fields = false;
00693 string iconvCharset;
00694
00695
00696 for(;;) {
00697 int cmd = getopt(argc, argv, "bd:D:hI:PSv");
00698 if( cmd == -1 )
00699 break;
00700
00701 switch( cmd )
00702 {
00703 case 'b':
00704 brief = true;
00705 break;
00706
00707 case 'd':
00708 m_compare_list.push_back(optarg);
00709 break;
00710
00711 case 'D':
00712 m_skip_list.push_back(optarg);
00713 break;
00714
00715 case 'P':
00716 AddParsersToCompare();
00717 break;
00718
00719 case 'S':
00720 if( show_parsers )
00721 show_fields = true;
00722 else
00723 show_parsers = true;
00724 break;
00725
00726 case 'I':
00727 iconvCharset = optarg;
00728 break;
00729
00730 case 'v':
00731 if( !m_verbose )
00732 m_verbose = true;
00733 else
00734 m_always_hex = true;
00735 break;
00736
00737 case 'h':
00738 default:
00739 Usage();
00740 return 0;
00741 }
00742 }
00743
00744 if( show_parsers ) {
00745 ShowParsers(show_fields, false);
00746 return 0;
00747 }
00748
00749 if( (optind + 2) > argc ) {
00750 Usage();
00751 return 0;
00752 }
00753
00754
00755
00756 m_tarpaths[0] = m_tarfiles[0] = argv[optind];
00757 m_tarpaths[1] = m_tarfiles[1] = argv[optind+1];
00758
00759 if( brief ) {
00760
00761 m_tarfiles[0] = "tar[0]";
00762 m_tarfiles[1] = "tar[1]";
00763 }
00764 else {
00765
00766 if( m_tarpaths[0].find('/') != string::npos )
00767 m_tarfiles[0] = m_tarpaths[0].substr(m_tarpaths[0].rfind('/') + 1);
00768 if( m_tarpaths[1].find('/') != string::npos )
00769 m_tarfiles[1] = m_tarpaths[1].substr(m_tarpaths[1].rfind('/') + 1);
00770
00771
00772 if( m_tarfiles[0] == m_tarfiles[1] ) {
00773
00774 m_tarfiles[0] = m_tarpaths[0];
00775 m_tarfiles[1] = m_tarpaths[1];
00776 }
00777 }
00778
00779
00780 cout << "tar[0] = " << m_tarpaths[0] << endl;
00781 cout << "tar[1] = " << m_tarpaths[1] << endl;
00782
00783
00784 Barry::Init(false);
00785
00786
00787 if( iconvCharset.size() ) {
00788 m_ic.reset( new IConverter(iconvCharset.c_str(), true) );
00789 }
00790
00791
00792 LoadTarballs();
00793
00794
00795 CompareDatabaseNames();
00796
00797
00798 CompareData();
00799
00800 return m_main_return;
00801 }
00802
00803 int main(int argc, char *argv[])
00804 {
00805 try {
00806 App app;
00807 return app.main(argc, argv);
00808 }
00809 catch( std::exception &e ) {
00810 cerr << "Exception: " << e.what() << endl;
00811 return 1;
00812 }
00813 }
00814