in fdbserver/fdbserver.actor.cpp [1028:1665]
void parseArgsInternal(int argc, char* argv[]) {
for (int a = 0; a < argc; a++) {
if (a)
commandLine += ' ';
commandLine += argv[a];
}
CSimpleOpt args(argc, argv, g_rgOptions, SO_O_EXACT | SO_O_HYPHEN_TO_UNDERSCORE);
if (argc == 1) {
printUsage(argv[0], false);
flushAndExit(FDB_EXIT_ERROR);
}
while (args.Next()) {
if (args.LastError() == SO_ARG_INVALID_DATA) {
fprintf(stderr, "ERROR: invalid argument to option `%s'\n", args.OptionText());
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
if (args.LastError() == SO_ARG_INVALID) {
fprintf(stderr, "ERROR: argument given for option `%s'\n", args.OptionText());
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
if (args.LastError() == SO_ARG_MISSING) {
fprintf(stderr, "ERROR: missing argument for option `%s'\n", args.OptionText());
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
if (args.LastError() == SO_OPT_INVALID) {
fprintf(stderr, "ERROR: unknown option: `%s'\n", args.OptionText());
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
if (args.LastError() != SO_SUCCESS) {
fprintf(stderr, "ERROR: error parsing options\n");
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
const char* sRole;
Optional<uint64_t> ti;
std::string argStr;
std::vector<std::string> tmpStrings;
switch (args.OptionId()) {
case OPT_HELP:
printUsage(argv[0], false);
flushAndExit(FDB_EXIT_SUCCESS);
break;
case OPT_DEVHELP:
printUsage(argv[0], true);
flushAndExit(FDB_EXIT_SUCCESS);
break;
case OPT_KNOB: {
Optional<std::string> knobName = extractPrefixedArgument("--knob", args.OptionSyntax());
if (!knobName.present()) {
fprintf(stderr, "ERROR: unable to parse knob option '%s'\n", args.OptionSyntax());
flushAndExit(FDB_EXIT_ERROR);
}
knobs.emplace_back(knobName.get(), args.OptionArg());
manualKnobOverrides[knobName.get()] = args.OptionArg();
break;
}
case OPT_PROFILER: {
Optional<std::string> profilerArg = extractPrefixedArgument("--profiler", args.OptionSyntax());
if (!profilerArg.present()) {
fprintf(stderr, "ERROR: unable to parse profiler option '%s'\n", args.OptionSyntax());
flushAndExit(FDB_EXIT_ERROR);
}
profilerConfig.emplace(profilerArg.get(), args.OptionArg());
break;
};
case OPT_UNITTESTPARAM: {
Optional<std::string> testArg = extractPrefixedArgument("--test", args.OptionSyntax());
if (!testArg.present()) {
fprintf(stderr, "ERROR: unable to parse unit test option '%s'\n", args.OptionSyntax());
flushAndExit(FDB_EXIT_ERROR);
}
testParams.set(testArg.get(), args.OptionArg());
break;
}
case OPT_LOCALITY: {
Optional<std::string> localityKey = extractPrefixedArgument("--locality", args.OptionSyntax());
if (!localityKey.present()) {
fprintf(stderr, "ERROR: unable to parse locality key '%s'\n", args.OptionSyntax());
flushAndExit(FDB_EXIT_ERROR);
}
Standalone<StringRef> key = StringRef(localityKey.get());
std::transform(key.begin(), key.end(), mutateString(key), ::tolower);
localities.set(key, Standalone<StringRef>(std::string(args.OptionArg())));
break;
}
case OPT_VERSION:
printVersion();
flushAndExit(FDB_EXIT_SUCCESS);
break;
case OPT_BUILD_FLAGS:
printBuildInformation();
flushAndExit(FDB_EXIT_SUCCESS);
case OPT_NOBUFSTDOUT:
setvbuf(stdout, nullptr, _IONBF, 0);
setvbuf(stderr, nullptr, _IONBF, 0);
break;
case OPT_BUFSTDOUTERR:
setvbuf(stdout, nullptr, _IOFBF, BUFSIZ);
setvbuf(stderr, nullptr, _IOFBF, BUFSIZ);
break;
case OPT_ROLE:
sRole = args.OptionArg();
if (!strcmp(sRole, "fdbd"))
role = ServerRole::FDBD;
else if (!strcmp(sRole, "simulation"))
role = ServerRole::Simulation;
else if (!strcmp(sRole, "test"))
role = ServerRole::Test;
else if (!strcmp(sRole, "multitest"))
role = ServerRole::MultiTester;
else if (!strcmp(sRole, "skiplisttest"))
role = ServerRole::SkipListTest;
else if (!strcmp(sRole, "search"))
role = ServerRole::SearchMutations;
else if (!strcmp(sRole, "dsltest"))
role = ServerRole::DSLTest;
else if (!strcmp(sRole, "versionedmaptest"))
role = ServerRole::VersionedMapTest;
else if (!strcmp(sRole, "createtemplatedb"))
role = ServerRole::CreateTemplateDatabase;
else if (!strcmp(sRole, "networktestclient"))
role = ServerRole::NetworkTestClient;
else if (!strcmp(sRole, "networktestserver"))
role = ServerRole::NetworkTestServer;
else if (!strcmp(sRole, "restore"))
role = ServerRole::Restore;
else if (!strcmp(sRole, "kvfileintegritycheck"))
role = ServerRole::KVFileIntegrityCheck;
else if (!strcmp(sRole, "kvfilegeneratesums"))
role = ServerRole::KVFileGenerateIOLogChecksums;
else if (!strcmp(sRole, "kvfiledump"))
role = ServerRole::KVFileDump;
else if (!strcmp(sRole, "consistencycheck"))
role = ServerRole::ConsistencyCheck;
else if (!strcmp(sRole, "unittests"))
role = ServerRole::UnitTests;
else {
fprintf(stderr, "ERROR: Unknown role `%s'\n", sRole);
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
break;
case OPT_PUBLICADDR:
argStr = args.OptionArg();
boost::split(tmpStrings, argStr, [](char c) { return c == ','; });
publicAddressStrs.insert(publicAddressStrs.end(), tmpStrings.begin(), tmpStrings.end());
break;
case OPT_LISTEN:
argStr = args.OptionArg();
boost::split(tmpStrings, argStr, [](char c) { return c == ','; });
listenAddressStrs.insert(listenAddressStrs.end(), tmpStrings.begin(), tmpStrings.end());
break;
case OPT_CONNFILE:
connFile = args.OptionArg();
break;
case OPT_LOGGROUP:
logGroup = args.OptionArg();
break;
case OPT_SEEDCONNFILE:
seedConnFile = args.OptionArg();
break;
case OPT_SEEDCONNSTRING:
seedConnString = args.OptionArg();
break;
#ifdef __linux__
case OPT_FILESYSTEM: {
fileSystemPath = args.OptionArg();
break;
}
case OPT_PROFILER_RSS_SIZE: {
const char* a = args.OptionArg();
char* end;
rsssize = strtoull(a, &end, 10);
if (*end) {
fprintf(stderr, "ERROR: Unrecognized memory size `%s'\n", a);
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
break;
}
#endif
case OPT_DATAFOLDER:
dataFolder = args.OptionArg();
break;
case OPT_LOGFOLDER:
logFolder = args.OptionArg();
break;
case OPT_NETWORKIMPL: {
const char* a = args.OptionArg();
if (!strcmp(a, "net2"))
useNet2 = true;
else if (!strcmp(a, "net2-threadpool")) {
useNet2 = true;
useThreadPool = true;
} else {
fprintf(stderr, "ERROR: Unknown network implementation `%s'\n", a);
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
break;
}
case OPT_TRACECLOCK: {
const char* a = args.OptionArg();
if (!strcmp(a, "realtime"))
g_trace_clock.store(TRACE_CLOCK_REALTIME);
else if (!strcmp(a, "now"))
g_trace_clock.store(TRACE_CLOCK_NOW);
else {
fprintf(stderr, "ERROR: Unknown clock source `%s'\n", a);
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
break;
}
case OPT_NUMTESTERS: {
const char* a = args.OptionArg();
if (!sscanf(a, "%d", &minTesterCount)) {
fprintf(stderr, "ERROR: Could not parse numtesters `%s'\n", a);
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
break;
}
case OPT_ROLLSIZE: {
const char* a = args.OptionArg();
ti = parse_with_suffix(a);
if (!ti.present()) {
fprintf(stderr, "ERROR: Could not parse logsize `%s'\n", a);
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
rollsize = ti.get();
break;
}
case OPT_MAXLOGSSIZE: {
const char* a = args.OptionArg();
ti = parse_with_suffix(a);
if (!ti.present()) {
fprintf(stderr, "ERROR: Could not parse maxlogssize `%s'\n", a);
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
maxLogsSize = ti.get();
maxLogsSizeSet = true;
break;
}
case OPT_MAXLOGS: {
const char* a = args.OptionArg();
char* end;
maxLogs = strtoull(a, &end, 10);
if (*end) {
fprintf(stderr, "ERROR: Unrecognized maximum number of logs `%s'\n", a);
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
maxLogsSet = true;
break;
}
#ifdef _WIN32
case OPT_PARENTPID: {
auto pid_str = args.OptionArg();
int parent_pid = atoi(pid_str);
auto pHandle = OpenProcess(SYNCHRONIZE, FALSE, parent_pid);
if (!pHandle) {
TraceEvent("ParentProcessOpenError").GetLastError();
fprintf(stderr, "Could not open parent process at pid %d (error %d)", parent_pid, GetLastError());
throw platform_error();
}
startThread(&parentWatcher, pHandle);
break;
}
case OPT_NEWCONSOLE:
FreeConsole();
AllocConsole();
freopen("CONIN$", "rb", stdin);
freopen("CONOUT$", "wb", stdout);
freopen("CONOUT$", "wb", stderr);
break;
case OPT_NOBOX:
SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX);
break;
#else
case OPT_PARENTPID: {
auto pid_str = args.OptionArg();
int* parent_pid = new (int);
*parent_pid = atoi(pid_str);
startThread(&parentWatcher, parent_pid);
break;
}
#endif
case OPT_TRACER: {
std::string arg = args.OptionArg();
std::string tracer;
std::transform(arg.begin(), arg.end(), std::back_inserter(tracer), [](char c) { return tolower(c); });
if (tracer == "none" || tracer == "disabled") {
openTracer(TracerType::DISABLED);
} else if (tracer == "logfile" || tracer == "file" || tracer == "log_file") {
openTracer(TracerType::LOG_FILE);
} else if (tracer == "network_lossy") {
openTracer(TracerType::NETWORK_LOSSY);
} else {
fprintf(stderr, "ERROR: Unknown or unsupported tracer: `%s'", args.OptionArg());
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
break;
}
case OPT_TESTFILE:
testFile = args.OptionArg();
break;
case OPT_KVFILE:
kvFile = args.OptionArg();
break;
case OPT_RESTARTING:
restarting = true;
break;
case OPT_RANDOMSEED: {
char* end;
randomSeed = (uint32_t)strtoul(args.OptionArg(), &end, 0);
if (*end) {
fprintf(stderr, "ERROR: Could not parse random seed `%s'\n", args.OptionArg());
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
break;
}
case OPT_MACHINEID: {
zoneId = std::string(args.OptionArg());
break;
}
case OPT_DCID: {
dcId = std::string(args.OptionArg());
break;
}
case OPT_MACHINE_CLASS:
sRole = args.OptionArg();
processClass = ProcessClass(sRole, ProcessClass::CommandLineSource);
if (processClass == ProcessClass::InvalidClass) {
fprintf(stderr, "ERROR: Unknown machine class `%s'\n", sRole);
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
break;
case OPT_KEY:
targetKey = args.OptionArg();
break;
case OPT_MEMLIMIT:
ti = parse_with_suffix(args.OptionArg(), "MiB");
if (!ti.present()) {
fprintf(stderr, "ERROR: Could not parse memory limit from `%s'\n", args.OptionArg());
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
memLimit = ti.get();
break;
case OPT_STORAGEMEMLIMIT:
ti = parse_with_suffix(args.OptionArg(), "MB");
if (!ti.present()) {
fprintf(stderr, "ERROR: Could not parse storage memory limit from `%s'\n", args.OptionArg());
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
storageMemLimit = ti.get();
break;
case OPT_CACHEMEMLIMIT:
ti = parse_with_suffix(args.OptionArg(), "MiB");
if (!ti.present()) {
fprintf(stderr, "ERROR: Could not parse cache memory limit from `%s'\n", args.OptionArg());
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
// SOMEDAY: ideally we'd have some better way to express that a knob should be elevated to formal
// parameter
knobs.emplace_back(
"page_cache_4k",
format("%ld", ti.get() / 4096 * 4096)); // The cache holds 4K pages, so we can truncate this to the
// next smaller multiple of 4K.
break;
case OPT_BUGGIFY:
if (!strcmp(args.OptionArg(), "on"))
buggifyEnabled = true;
else if (!strcmp(args.OptionArg(), "off"))
buggifyEnabled = false;
else {
fprintf(stderr, "ERROR: Unknown buggify state `%s'\n", args.OptionArg());
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
break;
case OPT_FAULT_INJECTION:
if (!strcmp(args.OptionArg(), "on"))
faultInjectionEnabled = true;
else if (!strcmp(args.OptionArg(), "off"))
faultInjectionEnabled = false;
else {
fprintf(stderr, "ERROR: Unknown fault injection state `%s'\n", args.OptionArg());
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
break;
case OPT_CRASHONERROR:
g_crashOnError = true;
break;
case OPT_TESTSERVERS:
testServersStr = args.OptionArg();
break;
case OPT_TEST_ON_SERVERS:
testOnServers = true;
break;
case OPT_METRICSCONNFILE:
metricsConnFile = args.OptionArg();
break;
case OPT_METRICSPREFIX:
metricsPrefix = args.OptionArg();
break;
case OPT_IO_TRUST_SECONDS: {
const char* a = args.OptionArg();
if (!sscanf(a, "%lf", &fileIoTimeout)) {
fprintf(stderr, "ERROR: Could not parse io_trust_seconds `%s'\n", a);
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
break;
}
case OPT_IO_TRUST_WARN_ONLY:
fileIoWarnOnly = true;
break;
case OPT_TRACE_FORMAT:
if (!selectTraceFormatter(args.OptionArg())) {
fprintf(stderr, "WARNING: Unrecognized trace format `%s'\n", args.OptionArg());
}
break;
case OPT_WHITELIST_BINPATH:
whitelistBinPaths = args.OptionArg();
break;
case OPT_BLOB_CREDENTIAL_FILE:
// Add blob credential following backup agent example
blobCredentials.push_back(args.OptionArg());
printf("blob credential file:%s\n", blobCredentials.back().c_str());
blobCredsFromENV = getenv("FDB_BLOB_CREDENTIALS");
if (blobCredsFromENV != nullptr) {
fprintf(stderr, "[WARNING] Set blob credetial via env variable is not tested yet\n");
TraceEvent(SevError, "FastRestoreGetBlobCredentialFile")
.detail("Reason", "Set blob credetial via env variable is not tested yet");
StringRef t((uint8_t*)blobCredsFromENV, strlen(blobCredsFromENV));
do {
StringRef file = t.eat(":");
if (file.size() != 0) {
blobCredentials.push_back(file.toString());
}
} while (t.size() != 0);
}
break;
case OPT_CONFIG_PATH:
configPath = args.OptionArg();
break;
case OPT_USE_TEST_CONFIG_DB:
configDBType = ConfigDBType::SIMPLE;
break;
case OPT_PRINT_SIMTIME:
printSimTime = true;
break;
#ifndef TLS_DISABLED
case TLSConfig::OPT_TLS_PLUGIN:
args.OptionArg();
break;
case TLSConfig::OPT_TLS_CERTIFICATES:
tlsConfig.setCertificatePath(args.OptionArg());
break;
case TLSConfig::OPT_TLS_PASSWORD:
tlsConfig.setPassword(args.OptionArg());
break;
case TLSConfig::OPT_TLS_CA_FILE:
tlsConfig.setCAPath(args.OptionArg());
break;
case TLSConfig::OPT_TLS_KEY:
tlsConfig.setKeyPath(args.OptionArg());
break;
case TLSConfig::OPT_TLS_VERIFY_PEERS:
tlsConfig.addVerifyPeers(args.OptionArg());
break;
#endif
}
}
try {
ProfilerConfig::instance().reset(profilerConfig);
} catch (ConfigError& e) {
printf("Error seting up profiler: %s", e.description.c_str());
flushAndExit(FDB_EXIT_ERROR);
}
if (seedConnString.length() && seedConnFile.length()) {
fprintf(
stderr, "%s\n", "--seed-cluster-file and --seed-connection-string may not both be specified at once.");
flushAndExit(FDB_EXIT_ERROR);
}
bool seedSpecified = seedConnFile.length() || seedConnString.length();
if (seedSpecified && !connFile.length()) {
fprintf(stderr,
"%s\n",
"If -seed-cluster-file or --seed-connection-string is specified, -C must be specified as well.");
flushAndExit(FDB_EXIT_ERROR);
}
if (metricsConnFile == connFile)
metricsConnFile = "";
if (metricsConnFile != "" && metricsPrefix == "") {
fprintf(stderr, "If a metrics cluster file is specified, a metrics prefix is required.\n");
flushAndExit(FDB_EXIT_ERROR);
}
bool autoPublicAddress =
std::any_of(publicAddressStrs.begin(), publicAddressStrs.end(), [](const std::string& addr) {
return StringRef(addr).startsWith(LiteralStringRef("auto:"));
});
if ((role != ServerRole::Simulation && role != ServerRole::CreateTemplateDatabase &&
role != ServerRole::KVFileIntegrityCheck && role != ServerRole::KVFileGenerateIOLogChecksums &&
role != ServerRole::KVFileDump && role != ServerRole::UnitTests) ||
autoPublicAddress) {
if (seedSpecified && !fileExists(connFile)) {
std::string connectionString = seedConnString.length() ? seedConnString : "";
ClusterConnectionString ccs;
if (seedConnFile.length()) {
try {
connectionString = readFileBytes(seedConnFile, MAX_CLUSTER_FILE_BYTES);
} catch (Error& e) {
fprintf(stderr,
"%s\n",
ClusterConnectionFile::getErrorString(std::make_pair(seedConnFile, false), e).c_str());
throw;
}
}
try {
ccs = ClusterConnectionString(connectionString);
} catch (Error& e) {
fprintf(stderr, "%s\n", ClusterConnectionString::getErrorString(connectionString, e).c_str());
throw;
}
connectionFile = makeReference<ClusterConnectionFile>(connFile, ccs);
} else {
std::pair<std::string, bool> resolvedClusterFile;
try {
resolvedClusterFile = ClusterConnectionFile::lookupClusterFileName(connFile);
connectionFile = makeReference<ClusterConnectionFile>(resolvedClusterFile.first);
} catch (Error& e) {
fprintf(stderr, "%s\n", ClusterConnectionFile::getErrorString(resolvedClusterFile, e).c_str());
throw;
}
}
// failmon?
}
try {
if (!publicAddressStrs.empty()) {
std::tie(publicAddresses, listenAddresses) =
buildNetworkAddresses(*connectionFile, publicAddressStrs, listenAddressStrs);
}
} catch (Error&) {
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
if (role == ServerRole::ConsistencyCheck) {
if (!publicAddressStrs.empty()) {
fprintf(stderr, "ERROR: Public address cannot be specified for consistency check processes\n");
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
auto publicIP = determinePublicIPAutomatically(connectionFile->getConnectionString());
publicAddresses.address = NetworkAddress(publicIP, ::getpid());
}
if (role == ServerRole::Simulation) {
Optional<bool> buggifyOverride = checkBuggifyOverride(testFile);
if (buggifyOverride.present())
buggifyEnabled = buggifyOverride.get();
}
if (role == ServerRole::SearchMutations && !targetKey) {
fprintf(stderr, "ERROR: please specify a target key\n");
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
if (role == ServerRole::NetworkTestClient && !testServersStr.size()) {
fprintf(stderr, "ERROR: please specify --testservers\n");
printHelpTeaser(argv[0]);
flushAndExit(FDB_EXIT_ERROR);
}
// Interpret legacy "maxLogs" option in the most sensible and unsurprising way we can while eliminating its code
// path
if (maxLogsSet) {
if (maxLogsSizeSet) {
// This is the case where both options are set and we must deconflict.
auto maxLogsAsSize = maxLogs * rollsize;
// If either was unlimited, then the safe option here is to take the larger one.
// This means that is one of the two options specified a limited amount of logging
// then the option that specified "unlimited" will be ignored.
if (maxLogsSize == 0 || maxLogs == 0)
maxLogsSize = std::max(maxLogsSize, maxLogsAsSize);
else
maxLogsSize = std::min(maxLogsSize, maxLogs * rollsize);
} else {
maxLogsSize = maxLogs * rollsize;
}
}
if (!zoneId.present() &&
!(localities.isPresent(LocalityData::keyZoneId) && localities.isPresent(LocalityData::keyMachineId))) {
machineId = getSharedMemoryMachineId().toString();
}
if (!localities.isPresent(LocalityData::keyZoneId))
localities.set(LocalityData::keyZoneId, zoneId.present() ? zoneId : machineId);
if (!localities.isPresent(LocalityData::keyMachineId))
localities.set(LocalityData::keyMachineId, zoneId.present() ? zoneId : machineId);
if (!localities.isPresent(LocalityData::keyDcId) && dcId.present())
localities.set(LocalityData::keyDcId, dcId);
}