CmdLineOptions parseCmdLineOptions()

in mcrouter/StandaloneUtils.cpp [215:385]


CmdLineOptions parseCmdLineOptions(int argc, char** argv, std::string pkgName) {
  CmdLineOptions res;

  CHECK(argc >= 1) << "invalid argc value";

  if (pkgName.empty()) {
    res.packageName = argv[0];
  } else {
    res.packageName = std::move(pkgName);
  }

  res.commandArgs = constructArgString(argc, argv);

  std::vector<option> longOptions = {
      {"verbosity", 1, nullptr, 'v'},
      {"help", 0, nullptr, 'h'},
      {"version", 0, nullptr, 'V'},
      {"validate-config", 2, nullptr, 0},
      {"proxy-threads", 1, nullptr, 0},
      {"service-name", 1, nullptr, 0},

      // Deprecated or not supported
      {"gets", 0, nullptr, 0},
      {"skip-sanity-checks", 0, nullptr, 0},
      {"retry-timeout", 1, nullptr, 0},
  };

  std::string optstring = "dD:v:hV";

  // Append auto-generated options to longOptions and optstring
  auto optionData = McrouterOptions::getOptionData();
  auto standaloneData = McrouterStandaloneOptions::getOptionData();
  auto combinedOptionData = optionData;
  combinedOptionData.insert(
      combinedOptionData.end(), standaloneData.begin(), standaloneData.end());
  for (auto& opt : combinedOptionData) {
    if (!opt.long_option.empty()) {
      // Toggle options can have optional argument
      int extraArgs = (opt.type == McrouterOptionData::Type::toggle ? 2 : 1);
      longOptions.push_back(
          {opt.long_option.c_str(), extraArgs, nullptr, opt.short_option});

      if (opt.short_option) {
        optstring.push_back(opt.short_option);
        if (extraArgs == 1) {
          optstring += ":";
        } else if (extraArgs == 2) {
          optstring += "::";
        }
      }
    }
  }

  longOptions.push_back({0, 0, 0, 0});

  int longIndex = -1;
  int c;
  while ((c = getopt_long(
              argc, argv, optstring.c_str(), longOptions.data(), &longIndex)) !=
         -1) {
    switch (c) {
      case 'v':
        FLAGS_v = folly::to<int>(optarg);
        break;

      case 'h':
        printUsageAndDie(argv[0], /* errorCode */ 0, pkgName);
      case 'V':
        printf("%s\n", MCROUTER_PACKAGE_STRING);
        exit(0);

      case 0:
      default:
        if (longIndex != -1 &&
            strcmp("constantly-reload-configs", longOptions[longIndex].name) ==
                0) {
          LOG(ERROR)
              << "CRITICAL: You have enabled constantly-reload-configs. "
                 "This undocumented feature is incredibly dangerous. "
                 "It will result in massively increased CPU consumption. "
                 "It will trigger lots of edge cases, surely causing hard failures. "
                 "If you're using this for *anything* other than testing, "
                 "please resign.";
        }

        // If the current short/long option is found in optData,
        // set it in the optDict and return true.  False otherwise.
        auto find_and_set =
            [&](const std::vector<McrouterOptionData>& optData,
                std::unordered_map<std::string, std::string>& optDict) {
              for (auto& opt : optData) {
                if (!opt.long_option.empty()) {
                  if ((opt.short_option && opt.short_option == c) ||
                      (!opt.long_option.empty() && longIndex != -1 &&
                       opt.long_option == longOptions[longIndex].name)) {
                    if (opt.type == McrouterOptionData::Type::toggle) {
                      optDict[opt.name] =
                          (optarg != nullptr
                               ? optarg
                               : (opt.default_value == "false" ? "1" : "0"));
                      ;
                    } else {
                      optDict[opt.name] = optarg;
                    }

                    return true;
                  }
                }
              }
              return false;
            };

        if (find_and_set(optionData, res.libmcrouterOptionsOverrides)) {
          break;
        }

        if (find_and_set(standaloneData, res.standaloneOptionsOverrides)) {
          break;
        }

        if (longIndex == -1) {
          res.unrecognizedOptions.insert(argv[optind - 1]);
        } else if (strcmp("proxy-threads", longOptions[longIndex].name) == 0) {
          if (strcmp(optarg, "auto") == 0) {
            int nprocs = sysconf(_SC_NPROCESSORS_ONLN);
            if (nprocs > 0) {
              res.libmcrouterOptionsOverrides["num_proxies"] =
                  std::to_string(nprocs);
            } else {
              LOG(INFO) << "Couldn't determine how many cores are available. "
                           "Defaulting to "
                        << DEFAULT_NUM_PROXIES << " proxy thread(s)";
            }
          } else {
            res.libmcrouterOptionsOverrides["num_proxies"] = optarg;
          }
        } else if (
            strcmp("validate-config", longOptions[longIndex].name) == 0) {
          if (!optarg || strcmp(optarg, "exit") == 0) {
            res.validateConfigMode = ValidateConfigMode::Exit;
          } else if (strcmp(optarg, "run") == 0) {
            res.validateConfigMode = ValidateConfigMode::Run;
          } else {
            LOG(ERROR) << "Invalid argument for --validate-config: '" << optarg
                       << "'. Ignoring the option.";
          }
        } else if (strcmp("retry-timeout", longOptions[longIndex].name) == 0) {
          LOG(WARNING) << "--retry-timeout is deprecated, use"
                          " --probe-timeout-initial";
          res.libmcrouterOptionsOverrides["probe_delay_initial_ms"] = optarg;
        } else if (strcmp("service-name", longOptions[longIndex].name) == 0) {
          // setup service name for standalone mcrouter
          res.serviceName = optarg;
        } else {
          res.unrecognizedOptions.insert(argv[optind - 1]);
        }
    }
    longIndex = -1;
  }

  // getopt permutes argv so that all non-options are at the end.
  // For now we only expect one non-option argument, so look at the last one.
  if (optind < argc && argv[optind]) {
    res.flavor = std::string(argv[optind]);
  }
  if (optind + 1 < argc) {
    LOG(ERROR) << "Expected only one non-option argument";
  }

  return res;
}