LogCabin
Storage/Tool.cc
Go to the documentation of this file.
00001 /* Copyright (c) 2012 Stanford University
00002  * Copyright (c) 2014-2015 Diego Ongaro
00003  *
00004  * Permission to use, copy, modify, and distribute this software for any
00005  * purpose with or without fee is hereby granted, provided that the above
00006  * copyright notice and this permission notice appear in all copies.
00007  *
00008  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
00009  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00010  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
00011  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00012  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00013  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
00014  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00015  */
00016 
00017 #include <getopt.h>
00018 #include <sys/file.h>
00019 #include <unistd.h>
00020 
00021 #include <functional>
00022 #include <iostream>
00023 #include <string>
00024 
00025 #include "build/Server/SnapshotMetadata.pb.h"
00026 #include "build/Server/SnapshotStateMachine.pb.h"
00027 #include "Core/Config.h"
00028 #include "Core/Debug.h"
00029 #include "Core/ProtoBuf.h"
00030 #include "Core/StringUtil.h"
00031 #include "Core/ThreadId.h"
00032 #include "Core/Util.h"
00033 #include "Storage/Layout.h"
00034 #include "Storage/Log.h"
00035 #include "Storage/LogFactory.h"
00036 #include "Storage/SnapshotFile.h"
00037 #include "Tree/Tree.h"
00038 
00039 namespace {
00040 
00041 using namespace LogCabin;
00042 
00043 /**
00044  * Parses argv for the main function.
00045  */
00046 class OptionParser {
00047   public:
00048     OptionParser(int& argc, char**& argv)
00049         : argc(argc)
00050         , argv(argv)
00051         , configFilename("logcabin.conf")
00052     {
00053         while (true) {
00054             static struct option longOptions[] = {
00055                {"config",  required_argument, NULL, 'c'},
00056                {"help",  no_argument, NULL, 'h'},
00057                {0, 0, 0, 0}
00058             };
00059             int c = getopt_long(argc, argv, "c:hi:", longOptions, NULL);
00060 
00061             // Detect the end of the options.
00062             if (c == -1)
00063                 break;
00064 
00065             switch (c) {
00066                 case 'h':
00067                     usage();
00068                     exit(0);
00069                 case 'c':
00070                     configFilename = optarg;
00071                     break;
00072                 case '?':
00073                 default:
00074                     // getopt_long already printed an error message.
00075                     usage();
00076                     exit(1);
00077             }
00078         }
00079 
00080         // We don't expect any additional command line arguments (not options).
00081         if (optind != argc) {
00082             usage();
00083             exit(1);
00084         }
00085     }
00086 
00087     void usage() {
00088         std::cout
00089             << "Dumps out the contents of LogCabin's storage directory "
00090             << "(the log and snapshot)."
00091             << std::endl
00092             << "This will refuse to run while LogCabin is running, since "
00093             << "it does the"
00094             << std::endl
00095             << "equivalent of a fsck for the log."
00096             << std::endl
00097             << std::endl
00098             << "This program is subject to change (it is not part of "
00099             << "LogCabin's stable API)."
00100             << std::endl
00101             << std::endl
00102 
00103             << "Usage: " << argv[0] << " [options]"
00104             << std::endl
00105             << std::endl
00106 
00107             << "Options:"
00108             << std::endl
00109 
00110             << "  -h, --help                   "
00111             << "Print this usage information"
00112             << std::endl
00113 
00114             << "  -c <file>, --config=<file>   "
00115             << "Set the path to the configuration file"
00116             << std::endl
00117             << "                               "
00118             << "[default: logcabin.conf]"
00119             << std::endl;
00120     }
00121 
00122     int& argc;
00123     char**& argv;
00124     std::string configFilename;
00125 };
00126 
00127 void
00128 dumpTree(const Tree::Tree& tree, const std::string& path = "/")
00129 {
00130     std::cout << path << std::endl;
00131     std::vector<std::string> children;
00132     tree.listDirectory(path, children);
00133     for (auto it = children.begin();
00134          it != children.end();
00135          ++it) {
00136         if (Core::StringUtil::endsWith(*it, "/")) {
00137             dumpTree(tree, path + *it);
00138         } else {
00139             std::string contents;
00140             tree.read(path + *it, contents);
00141             std::cout << path << *it << " : " << contents << std::endl;
00142         }
00143     }
00144 }
00145 
00146 void
00147 readSnapshot(Storage::Layout& storageLayout)
00148 {
00149     std::unique_ptr<Storage::SnapshotFile::Reader> reader;
00150     try {
00151         reader.reset(new Storage::SnapshotFile::Reader(storageLayout));
00152     } catch (const std::runtime_error& e) { // file not found
00153         NOTICE("%s", e.what());
00154         return;
00155     }
00156 
00157     { // Check that this snapshot uses format version 1
00158         uint8_t version = 0;
00159         uint64_t bytesRead = reader->readRaw(&version, sizeof(version));
00160         if (bytesRead < 1) {
00161             PANIC("Found completely empty snapshot file (it doesn't even "
00162                   "have a version field)");
00163         } else {
00164             if (version != 1) {
00165                 PANIC("Snapshot format version read was %u, but this code "
00166                       "can only read version 1",
00167                       version);
00168             }
00169         }
00170     }
00171 
00172     { // read header protobuf from stream
00173         Server::SnapshotMetadata::Header header;
00174         std::string error = reader->readMessage(header);
00175         if (!error.empty()) {
00176             PANIC("couldn't read snapshot header: %s",
00177                   error.c_str());
00178         }
00179         NOTICE("Snapshot header start");
00180         std::cout << Core::ProtoBuf::dumpString(header) << std::endl;
00181         NOTICE("Snapshot header end");
00182     }
00183 
00184     { // Check that the state machine part of the snapshot uses format
00185       // version 1
00186         uint8_t version = 0;
00187         uint64_t bytesRead = reader->readRaw(&version, sizeof(version));
00188         if (bytesRead < 1) {
00189             PANIC("Snapshot file too short (no state machine version "
00190                   "field)");
00191         } else {
00192             if (version != 1) {
00193                 PANIC("State machine format version in snapshot read was "
00194                       "%u, but this code can only read version 1",
00195                       version);
00196             }
00197         }
00198     }
00199 
00200     { // Load snapshot header
00201         Server::SnapshotStateMachine::Header header;
00202         std::string error = reader->readMessage(header);
00203         if (!error.empty()) {
00204             PANIC("Couldn't read state machine header from snapshot: %s",
00205                   error.c_str());
00206         }
00207         NOTICE("Snapshot state machine header start");
00208         std::cout << Core::ProtoBuf::dumpString(header) << std::endl;
00209         NOTICE("Snapshot state machine header end");
00210 
00211     }
00212 
00213     { // read Tree from stream
00214         Tree::Tree tree;
00215         tree.loadSnapshot(*reader);
00216         NOTICE("Snapshot tree start");
00217         dumpTree(tree);
00218         NOTICE("Snapshot tree end");
00219     }
00220 }
00221 
00222 } // anonymous namespace
00223 
00224 int
00225 main(int argc, char** argv)
00226 {
00227     using namespace LogCabin;
00228 
00229     try {
00230 
00231         Core::Util::Finally _(google::protobuf::ShutdownProtobufLibrary);
00232         Core::ThreadId::setName("main");
00233 
00234         // Parse command line args.
00235         OptionParser options(argc, argv);
00236 
00237         NOTICE("Using config file %s", options.configFilename.c_str());
00238         Core::Config config;
00239         config.readFile(options.configFilename.c_str());
00240 
00241         Core::Debug::setLogPolicy(
00242             Core::Debug::logPolicyFromString(
00243                 config.read<std::string>("logPolicy", "NOTICE")));
00244 
00245         uint64_t serverId = config.read<uint64_t>("serverId");
00246         NOTICE("Server ID is %lu", serverId);
00247 
00248         Storage::Layout storageLayout;
00249         storageLayout.init(config, serverId);
00250 
00251         NOTICE("Opening log at %s", storageLayout.serverDir.path.c_str());
00252         {
00253             std::unique_ptr<Storage::Log> log =
00254                 Storage::LogFactory::makeLog(config, storageLayout);
00255             NOTICE("Log contents start");
00256             std::cout << *log << std::endl;
00257             NOTICE("Log contents end");
00258         }
00259 
00260         NOTICE("Reading snapshot at %s", storageLayout.serverDir.path.c_str());
00261         readSnapshot(storageLayout);
00262 
00263         return 0;
00264 
00265     } catch (const Core::Config::Exception& e) {
00266         ERROR("Fatal exception from config file: %s",
00267               e.what());
00268     }
00269 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines