LogCabin
|
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 }