// ---------------------------------------------------------------------- // File: Configuration.cc // Author: Georgios Bitzes - CERN // ---------------------------------------------------------------------- /************************************************************************ * quarkdb - a redis-like highly available key-value store * * Copyright (C) 2016 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ #include #include #include #include "config/ConfigurationReader.hh" #include "Configuration.hh" #include "utils/Macros.hh" #include "utils/FileUtils.hh" #include "utils/StringUtils.hh" #include "Utils.hh" using namespace quarkdb; bool Configuration::fromFile(const std::string &filename, Configuration &out) { qdb_log("Reading configuration file from " << filename); std::string contents; if(!readFile(filename, contents)) { qdb_error("Could not read configuration file: " << filename); return false; } return Configuration::fromString(contents, out); } static bool fetchSingle(ConfigurationReader &reader, std::string &dest) { reader.advanceWord(); if(reader.eof()) { return false; } dest = reader.getCurrentWord(); if(dest.empty()) { return false; } return true; } static bool parseMode(const std::string &buffer, Mode &mode) { if(buffer == "standalone") { mode = Mode::standalone; } else if(buffer == "raft") { mode = Mode::raft; } else if(buffer == "bulkload") { mode = Mode::bulkload; } else { qdb_log("Unknown mode: " << quotes(buffer)); return false; } return true; } static bool parseBool(const std::string &buffer, bool &val) { if(buffer == "true") { val = true; return true; } else if(buffer == "false") { val = false; return true; } qdb_log("Cannot convert to boolean: " << quotes(buffer)); return false; } static bool parseTraceLevel(const std::string &buffer, TraceLevel &trace) { if(buffer == "off") { trace = TraceLevel::off; } else if(buffer == "error") { trace = TraceLevel::error; } else if(buffer == "warn" || buffer == "warning") { trace = TraceLevel::warning; } else if(buffer == "info") { trace = TraceLevel::info; } else if(buffer == "debug" || buffer == "all") { trace = TraceLevel::debug; } else { qdb_log("Unknown trace level: " << quotes(buffer)); return false; } return true; } bool Configuration::fromReader(ConfigurationReader &reader, Configuration &out) { while(!reader.eof()) { std::string current = reader.getCurrentWord(); bool isMine = StringUtils::startsWith(current, "redis."); if(!isMine) { reader.advanceLine(); continue; } current = std::string(current.begin()+6, current.end()); bool success = false; std::string buffer; if(StringUtils::startsWith("mode", current)) { success = fetchSingle(reader, buffer) && parseMode(buffer, out.mode); } else if(StringUtils::startsWith(current, "database")) { success = fetchSingle(reader, out.database); } else if(StringUtils::startsWith(current, "myself")) { success = fetchSingle(reader, buffer) && parseServer(buffer, out.myself); } else if(StringUtils::startsWith(current, "trace")) { success = fetchSingle(reader, buffer) && parseTraceLevel(buffer, out.trace); } else if(StringUtils::startsWith(current, "certificate")) { success = fetchSingle(reader, out.certificatePath); } else if(StringUtils::startsWith(current, "certificate_key")) { success = fetchSingle(reader, out.certificateKeyPath); } else if(StringUtils::startsWith(current, "write_ahead_log")) { success = fetchSingle(reader, buffer) && parseBool(buffer, out.writeAheadLog); } else if(StringUtils::startsWith(current, "password_file")) { success = fetchSingle(reader, out.passwordFilePath); } else if(StringUtils::startsWith(current, "password")) { success = fetchSingle(reader, out.password); } else if(StringUtils::startsWith(current, "require_password_for_localhost")) { success = fetchSingle(reader, buffer) && parseBool(buffer, out.requirePasswordForLocalhost); } else { qdb_warn("Error when parsing configuration - unknown option " << quotes(current)); return false; } if(!success) { qdb_warn("Error when parsing configuration option " << quotes("redis." << current)); return false; } reader.advanceLine(); } return out.isValid(); } bool Configuration::fromString(const std::string &str, Configuration &out) { ConfigurationReader reader(str); return Configuration::fromReader(reader, out); } bool Configuration::isValid() { if(database.empty()) { qdb_log("redis.database must be specified."); return false; } bool raft = (mode == Mode::raft); if(raft == myself.hostname.empty()) { qdb_log("redis.myself is required when using raft and is incompatible with rocksdb"); return false; } if(database[database.size()-1] == '/') { qdb_log("redis.database cannot contain trailing slashes"); return false; } if(certificatePath.empty() != certificateKeyPath.empty()) { qdb_log("Both the TLS certificate and key must be supplied."); return false; } if(!passwordFilePath.empty() && !password.empty()) { qdb_log("Cannot both specify redis.password_file and redis.password, choose one or the other"); return false; } if(password.empty() && passwordFilePath.empty() && requirePasswordForLocalhost) { qdb_log("Cannot require password for localhost, when no password has been set!"); return false; } return true; } std::string Configuration::extractPasswordOrDie() const { qdb_assert(passwordFilePath.empty() || password.empty()); if(!password.empty()) { return password; } if(passwordFilePath.empty()) { return ""; } std::string contents; if(!readPasswordFile(passwordFilePath, contents)) { qdb_throw("Could not read password file: " << passwordFilePath); } return contents; }