| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| #include "kudu/util/flags.h" |
| |
| #include <strings.h> |
| #include <sys/stat.h> |
| #include <unistd.h> // IWYU pragma: keep |
| |
| #include <algorithm> |
| #include <cstdlib> |
| #include <cstring> |
| #include <functional> |
| #include <iostream> |
| #include <iterator> |
| #include <map> |
| #include <string> |
| #include <unordered_set> |
| #include <utility> |
| #include <vector> |
| |
| #include <gflags/gflags.h> |
| #include <glog/logging.h> |
| #ifdef TCMALLOC_ENABLED |
| #include <gperftools/heap-profiler.h> |
| #endif |
| |
| #include "kudu/gutil/macros.h" |
| #include "kudu/gutil/map-util.h" |
| #include "kudu/gutil/stringprintf.h" |
| #include "kudu/gutil/strings/join.h" |
| #include "kudu/gutil/strings/numbers.h" |
| #include "kudu/gutil/strings/split.h" |
| #include "kudu/gutil/strings/stringpiece.h" |
| #include "kudu/gutil/strings/substitute.h" |
| #include "kudu/util/flag_tags.h" |
| #include "kudu/util/flag_validators.h" |
| #include "kudu/util/logging.h" |
| #include "kudu/util/metrics.h" |
| #include "kudu/util/os-util.h" |
| #include "kudu/util/path_util.h" |
| #include "kudu/util/string_case.h" |
| #include "kudu/util/url-coding.h" |
| #include "kudu/util/version_info.h" |
| |
| using google::CommandLineFlagInfo; |
| |
| using std::cout; |
| using std::endl; |
| using std::string; |
| using std::ostringstream; |
| using std::unordered_set; |
| using std::vector; |
| |
| using strings::Substitute; |
| |
| // Because every binary initializes its flags here, we use it as a convenient place |
| // to offer some global flags as well. |
| DEFINE_bool(dump_metrics_json, false, |
| "Dump a JSON document describing all of the metrics which may be emitted " |
| "by this binary."); |
| TAG_FLAG(dump_metrics_json, hidden); |
| |
| DEFINE_bool(dump_metrics_xml, false, |
| "Dump an XML document describing all of the metrics which may be emitted " |
| "by this binary."); |
| TAG_FLAG(dump_metrics_xml, hidden); |
| |
| #ifdef TCMALLOC_ENABLED |
| DEFINE_bool(enable_process_lifetime_heap_profiling, false, "Enables heap " |
| "profiling for the lifetime of the process. Profile output will be stored in the " |
| "directory specified by -heap_profile_path."); |
| TAG_FLAG(enable_process_lifetime_heap_profiling, stable); |
| TAG_FLAG(enable_process_lifetime_heap_profiling, advanced); |
| |
| DEFINE_string(heap_profile_path, "", "Output path to store heap profiles. If not set " \ |
| "profiles are stored in /tmp/<process-name>.<pid>.<n>.heap."); |
| TAG_FLAG(heap_profile_path, stable); |
| TAG_FLAG(heap_profile_path, advanced); |
| |
| DEFINE_int64(heap_sample_every_n_bytes, 0, |
| "Enable heap occupancy sampling. If this flag is set to some positive " |
| "value N, a memory allocation will be sampled approximately every N bytes. " |
| "Lower values of N incur larger overhead but give more accurate results. " |
| "A value such as 524288 (512KB) is a reasonable choice with relatively " |
| "low overhead."); |
| TAG_FLAG(heap_sample_every_n_bytes, advanced); |
| TAG_FLAG(heap_sample_every_n_bytes, experimental); |
| #endif |
| |
| DEFINE_bool(disable_core_dumps, false, "Disable core dumps when this process crashes."); |
| TAG_FLAG(disable_core_dumps, advanced); |
| TAG_FLAG(disable_core_dumps, evolving); |
| |
| DEFINE_string(umask, "077", |
| "The umask that will be used when creating files and directories. " |
| "Permissions of top-level data directories will also be modified at " |
| "start-up to conform to the given umask. Changing this value may " |
| "enable unauthorized local users to read or modify data stored by Kudu."); |
| TAG_FLAG(umask, advanced); |
| |
| static bool ValidateUmask(const char* /*flagname*/, const string& value) { |
| uint32_t parsed; |
| if (!safe_strtou32_base(value.c_str(), &parsed, 8)) { |
| LOG(ERROR) << "Invalid umask: must be an octal string"; |
| return false; |
| } |
| |
| // Verify that the umask doesn't restrict the permissions of the owner. |
| // If it did, we'd end up creating files that we can't read. |
| if ((parsed & 0700) != 0) { |
| LOG(ERROR) << "Invalid umask value: must not restrict owner permissions"; |
| return false; |
| } |
| return true; |
| } |
| |
| DEFINE_validator(umask, &ValidateUmask); |
| |
| DEFINE_bool(unlock_experimental_flags, false, |
| "Unlock flags marked as 'experimental'. These flags are not guaranteed to " |
| "be maintained across releases of Kudu, and may enable features or behavior " |
| "known to be unstable. Use at your own risk."); |
| TAG_FLAG(unlock_experimental_flags, advanced); |
| TAG_FLAG(unlock_experimental_flags, stable); |
| |
| DEFINE_bool(unlock_unsafe_flags, false, |
| "Unlock flags marked as 'unsafe'. These flags are not guaranteed to " |
| "be maintained across releases of Kudu, and enable features or behavior " |
| "known to be unsafe. Use at your own risk."); |
| TAG_FLAG(unlock_unsafe_flags, advanced); |
| TAG_FLAG(unlock_unsafe_flags, stable); |
| |
| DEFINE_string(redact, "all", |
| "Comma-separated list that controls redaction context. Supported options " |
| "are 'all','log', and 'none'. If 'all' is specified, sensitive data " |
| "(sensitive configuration flags and row data) will be redacted from " |
| "the web UI as well as glog and error messages. If 'log' is specified, " |
| "sensitive data will only be redacted from glog and error messages. " |
| "If 'none' is specified, no redaction will occur."); |
| TAG_FLAG(redact, advanced); |
| TAG_FLAG(redact, evolving); |
| |
| static bool ValidateRedact(const char* /*flagname*/, const string& value) { |
| kudu::g_should_redact = kudu::RedactContext::NONE; |
| |
| // Flag value is case insensitive. |
| string redact_flags; |
| kudu::ToUpperCase(value, &redact_flags); |
| |
| // 'all', 'none', and '' must be specified without any other option. |
| if (redact_flags == "ALL") { |
| kudu::g_should_redact = kudu::RedactContext::ALL; |
| return true; |
| } |
| if (redact_flags == "NONE" || redact_flags.empty()) { |
| return true; |
| } |
| |
| for (const auto& t : strings::Split(redact_flags, ",", strings::SkipEmpty())) { |
| if (t == "LOG") { |
| kudu::g_should_redact = kudu::RedactContext::LOG; |
| } else if (t == "ALL" || t == "NONE") { |
| LOG(ERROR) << "Invalid redaction options: " |
| << value << ", '" << t << "' must be specified by itself."; |
| return false; |
| } else { |
| LOG(ERROR) << "Invalid redaction context: " << t << |
| ". Available types are 'all', 'log', and 'none'."; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| DEFINE_validator(redact, &ValidateRedact); |
| // Tag a bunch of the flags that we inherit from glog/gflags. |
| |
| //------------------------------------------------------------ |
| // GLog flags |
| //------------------------------------------------------------ |
| // Most of these are considered stable. The ones related to email are |
| // marked unsafe because sending email inline from a server is a pretty |
| // bad idea. |
| DECLARE_string(alsologtoemail); |
| TAG_FLAG(alsologtoemail, hidden); |
| TAG_FLAG(alsologtoemail, unsafe); |
| |
| // --alsologtostderr is deprecated in favor of --stderrthreshold |
| DECLARE_bool(alsologtostderr); |
| TAG_FLAG(alsologtostderr, hidden); |
| TAG_FLAG(alsologtostderr, runtime); |
| |
| DECLARE_bool(colorlogtostderr); |
| TAG_FLAG(colorlogtostderr, stable); |
| TAG_FLAG(colorlogtostderr, runtime); |
| |
| DECLARE_bool(drop_log_memory); |
| TAG_FLAG(drop_log_memory, advanced); |
| TAG_FLAG(drop_log_memory, runtime); |
| |
| DECLARE_string(log_backtrace_at); |
| TAG_FLAG(log_backtrace_at, advanced); |
| |
| DECLARE_string(log_dir); |
| TAG_FLAG(log_dir, stable); |
| |
| DECLARE_string(log_link); |
| TAG_FLAG(log_link, stable); |
| TAG_FLAG(log_link, advanced); |
| |
| DECLARE_bool(log_prefix); |
| TAG_FLAG(log_prefix, stable); |
| TAG_FLAG(log_prefix, advanced); |
| TAG_FLAG(log_prefix, runtime); |
| |
| DECLARE_int32(logbuflevel); |
| TAG_FLAG(logbuflevel, advanced); |
| TAG_FLAG(logbuflevel, runtime); |
| DECLARE_int32(logbufsecs); |
| TAG_FLAG(logbufsecs, advanced); |
| TAG_FLAG(logbufsecs, runtime); |
| |
| DECLARE_int32(logemaillevel); |
| TAG_FLAG(logemaillevel, hidden); |
| TAG_FLAG(logemaillevel, unsafe); |
| |
| DECLARE_string(logmailer); |
| TAG_FLAG(logmailer, hidden); |
| |
| DECLARE_bool(logtostderr); |
| TAG_FLAG(logtostderr, stable); |
| TAG_FLAG(logtostderr, runtime); |
| |
| DECLARE_uint32(max_log_size); |
| TAG_FLAG(max_log_size, stable); |
| TAG_FLAG(max_log_size, runtime); |
| |
| DECLARE_int32(minloglevel); |
| TAG_FLAG(minloglevel, stable); |
| TAG_FLAG(minloglevel, advanced); |
| TAG_FLAG(minloglevel, runtime); |
| |
| DECLARE_int32(stderrthreshold); |
| TAG_FLAG(stderrthreshold, stable); |
| TAG_FLAG(stderrthreshold, advanced); |
| TAG_FLAG(stderrthreshold, runtime); |
| |
| DECLARE_bool(stop_logging_if_full_disk); |
| TAG_FLAG(stop_logging_if_full_disk, stable); |
| TAG_FLAG(stop_logging_if_full_disk, advanced); |
| TAG_FLAG(stop_logging_if_full_disk, runtime); |
| |
| DECLARE_int32(v); |
| TAG_FLAG(v, stable); |
| TAG_FLAG(v, advanced); |
| TAG_FLAG(v, runtime); |
| |
| DECLARE_string(vmodule); |
| TAG_FLAG(vmodule, stable); |
| TAG_FLAG(vmodule, advanced); |
| |
| DECLARE_bool(symbolize_stacktrace); |
| TAG_FLAG(symbolize_stacktrace, stable); |
| TAG_FLAG(symbolize_stacktrace, runtime); |
| TAG_FLAG(symbolize_stacktrace, advanced); |
| |
| //------------------------------------------------------------ |
| // GFlags flags |
| //------------------------------------------------------------ |
| DECLARE_string(flagfile); |
| TAG_FLAG(flagfile, stable); |
| |
| DECLARE_string(fromenv); |
| TAG_FLAG(fromenv, stable); |
| TAG_FLAG(fromenv, advanced); |
| |
| DECLARE_string(tryfromenv); |
| TAG_FLAG(tryfromenv, stable); |
| TAG_FLAG(tryfromenv, advanced); |
| |
| DECLARE_string(undefok); |
| TAG_FLAG(undefok, stable); |
| TAG_FLAG(undefok, advanced); |
| |
| DECLARE_int32(tab_completion_columns); |
| TAG_FLAG(tab_completion_columns, stable); |
| TAG_FLAG(tab_completion_columns, hidden); |
| |
| DECLARE_string(tab_completion_word); |
| TAG_FLAG(tab_completion_word, stable); |
| TAG_FLAG(tab_completion_word, hidden); |
| |
| DECLARE_bool(help); |
| TAG_FLAG(help, stable); |
| |
| DECLARE_bool(helpfull); |
| // We hide -helpfull because it's the same as -help for now. |
| TAG_FLAG(helpfull, stable); |
| TAG_FLAG(helpfull, hidden); |
| |
| DECLARE_string(helpmatch); |
| TAG_FLAG(helpmatch, stable); |
| TAG_FLAG(helpmatch, advanced); |
| |
| DECLARE_string(helpon); |
| TAG_FLAG(helpon, stable); |
| TAG_FLAG(helpon, advanced); |
| |
| DECLARE_bool(helppackage); |
| TAG_FLAG(helppackage, stable); |
| TAG_FLAG(helppackage, advanced); |
| |
| DECLARE_bool(helpshort); |
| TAG_FLAG(helpshort, stable); |
| TAG_FLAG(helpshort, advanced); |
| |
| DECLARE_bool(helpxml); |
| TAG_FLAG(helpxml, stable); |
| TAG_FLAG(helpxml, advanced); |
| |
| DECLARE_bool(version); |
| TAG_FLAG(version, stable); |
| |
| //------------------------------------------------------------ |
| // TCMalloc flags. |
| // These are tricky because tcmalloc doesn't use gflags. So we have to |
| // reach into its internal namespace. |
| //------------------------------------------------------------ |
| #define TCM_NAMESPACE FLAG__namespace_do_not_use_directly_use_DECLARE_int64_instead |
| namespace TCM_NAMESPACE { |
| extern int64_t FLAGS_tcmalloc_sample_parameter; |
| } // namespace TCM_NAMESPACE |
| |
| namespace kudu { |
| |
| // After flags have been parsed, the umask value is filled in here. |
| uint32_t g_parsed_umask = -1; |
| |
| namespace { |
| |
| void AppendXMLTag(const char* tag, const string& txt, string* r) { |
| strings::SubstituteAndAppend(r, "<$0>$1</$0>", tag, EscapeForHtmlToString(txt)); |
| } |
| |
| static string DescribeOneFlagInXML(const CommandLineFlagInfo& flag) { |
| unordered_set<string> tags; |
| GetFlagTags(flag.name, &tags); |
| |
| string r("<flag>"); |
| AppendXMLTag("file", flag.filename, &r); |
| AppendXMLTag("name", flag.name, &r); |
| AppendXMLTag("meaning", flag.description, &r); |
| AppendXMLTag("default", flag.default_value, &r); |
| AppendXMLTag("current", flag.current_value, &r); |
| AppendXMLTag("type", flag.type, &r); |
| AppendXMLTag("tags", JoinStrings(tags, ","), &r); |
| r += "</flag>"; |
| return r; |
| } |
| |
| void DumpFlagsXML() { |
| vector<CommandLineFlagInfo> flags; |
| GetAllFlags(&flags); |
| |
| cout << "<?xml version=\"1.0\"?>" << endl; |
| cout << "<AllFlags>" << endl; |
| cout << strings::Substitute( |
| "<program>$0</program>", |
| EscapeForHtmlToString(BaseName(google::ProgramInvocationShortName()))) << endl; |
| cout << strings::Substitute( |
| "<usage>$0</usage>", |
| EscapeForHtmlToString(google::ProgramUsage())) << endl; |
| |
| for (const CommandLineFlagInfo& flag : flags) { |
| cout << DescribeOneFlagInXML(flag) << endl; |
| } |
| |
| cout << "</AllFlags>" << endl; |
| } |
| |
| // Check that, if any flags tagged with 'tag' have been specified to |
| // non-default values, that 'unlocked' is true. If so (i.e. if the |
| // flags have been appropriately unlocked), emits a warning message |
| // for each flag and returns false. Otherwise, emits an error message |
| // and returns true. |
| bool CheckFlagsAndWarn(const string& tag, bool unlocked) { |
| vector<CommandLineFlagInfo> flags; |
| GetAllFlags(&flags); |
| |
| int use_count = 0; |
| for (const auto& f : flags) { |
| if (f.is_default) continue; |
| unordered_set<string> tags; |
| GetFlagTags(f.name, &tags); |
| if (!ContainsKey(tags, tag)) continue; |
| |
| if (unlocked) { |
| LOG(WARNING) << "Enabled " << tag << " flag: --" << f.name << "=" << f.current_value; |
| } else { |
| LOG(ERROR) << "Flag --" << f.name << " is " << tag << " and unsupported."; |
| use_count++; |
| } |
| } |
| |
| if (!unlocked && use_count > 0) { |
| LOG(ERROR) << use_count << " " << tag << " flag(s) in use."; |
| LOG(ERROR) << "Use --unlock_" << tag << "_flags to proceed at your own risk."; |
| return true; |
| } |
| return false; |
| } |
| |
| // Check that any flags specified on the command line are allowed |
| // to be set. This ensures that, if the user is using any unsafe |
| // or experimental flags, they have explicitly unlocked them. |
| void CheckFlagsAllowed() { |
| bool should_exit = false; |
| should_exit |= CheckFlagsAndWarn("unsafe", FLAGS_unlock_unsafe_flags); |
| should_exit |= CheckFlagsAndWarn("experimental", FLAGS_unlock_experimental_flags); |
| if (should_exit) { |
| exit(1); |
| } |
| } |
| |
| // Run 'late phase' custom validators: these can be run only when all flags are |
| // already parsed and individually validated. |
| void RunCustomValidators() { |
| const auto& validators(GetFlagValidators()); |
| bool found_inconsistency = false; |
| for (const auto& e : validators) { |
| found_inconsistency |= !e.second(); |
| } |
| if (found_inconsistency) { |
| LOG(ERROR) << "Detected inconsistency in command-line flags; exiting"; |
| exit(1); |
| } |
| } |
| |
| void SetUmask() { |
| // We already validated with a nice error message using the ValidateUmask |
| // FlagValidator above. |
| CHECK(safe_strtou32_base(FLAGS_umask.c_str(), &g_parsed_umask, 8)); |
| uint32_t old_mask = umask(g_parsed_umask); |
| if (old_mask != g_parsed_umask) { |
| VLOG(2) << "Changed umask from " << StringPrintf("%03o", old_mask) << " to " |
| << StringPrintf("%03o", g_parsed_umask); |
| } |
| } |
| |
| } // anonymous namespace |
| |
| // If --redact indicates, redact the flag tagged as 'sensitive'. |
| // Otherwise, return its value as-is. If EscapeMode is set to HTML, |
| // return HTML escaped string. |
| string CheckFlagAndRedact(const CommandLineFlagInfo& flag, EscapeMode mode) { |
| string ret_value; |
| unordered_set<string> tags; |
| GetFlagTags(flag.name, &tags); |
| |
| if (ContainsKey(tags, "sensitive") && KUDU_SHOULD_REDACT()) { |
| ret_value = kRedactionMessage; |
| } else { |
| ret_value = flag.current_value; |
| } |
| if (mode == EscapeMode::HTML) { |
| ret_value = EscapeForHtmlToString(ret_value); |
| } |
| return ret_value; |
| } |
| |
| int ParseCommandLineFlags(int* argc, char*** argv, bool remove_flags) { |
| // The logbufsecs default is 30 seconds which is a bit too long. |
| google::SetCommandLineOptionWithMode("logbufsecs", "5", |
| google::FlagSettingMode::SET_FLAGS_DEFAULT); |
| |
| int ret = google::ParseCommandLineNonHelpFlags(argc, argv, remove_flags); |
| HandleCommonFlags(); |
| ValidateFlags(); |
| return ret; |
| } |
| |
| void HandleCommonFlags() { |
| if (FLAGS_helpxml) { |
| DumpFlagsXML(); |
| exit(1); |
| } else if (FLAGS_dump_metrics_json) { |
| MetricPrototypeRegistry::get()->WriteAsJson(); |
| exit(0); |
| } else if (FLAGS_dump_metrics_xml) { |
| MetricPrototypeRegistry::get()->WriteAsXML(); |
| exit(0); |
| } else if (FLAGS_version) { |
| cout << VersionInfo::GetAllVersionInfo() << endl; |
| exit(0); |
| } |
| |
| google::HandleCommandLineHelpFlags(); |
| |
| if (FLAGS_disable_core_dumps) { |
| DisableCoreDumps(); |
| } |
| |
| SetUmask(); |
| |
| #ifdef TCMALLOC_ENABLED |
| if (FLAGS_heap_profile_path.empty()) { |
| FLAGS_heap_profile_path = strings::Substitute( |
| "/tmp/$0.$1", google::ProgramInvocationShortName(), getpid()); |
| } |
| |
| if (FLAGS_enable_process_lifetime_heap_profiling) { |
| HeapProfilerStart(FLAGS_heap_profile_path.c_str()); |
| } |
| // Set the internal tcmalloc flag unless it was already set using the built-in |
| // environment-variable-based method. It doesn't appear that this is settable |
| // in any less hacky fashion. |
| if (!getenv("TCMALLOC_SAMPLE_PARAMETER")) { |
| TCM_NAMESPACE::FLAGS_tcmalloc_sample_parameter = FLAGS_heap_sample_every_n_bytes; |
| } else if (!google::GetCommandLineFlagInfoOrDie("heap_sample_every_n_bytes").is_default) { |
| LOG(ERROR) << "Heap sampling configured using both --heap-sample-every-n-bytes and " |
| << "TCMALLOC_SAMPLE_PARAMETER. Ignoring command line flag."; |
| } |
| #endif |
| } |
| |
| void ValidateFlags() { |
| CheckFlagsAllowed(); |
| RunCustomValidators(); |
| } |
| |
| string CommandlineFlagsIntoString(EscapeMode mode) { |
| string ret_value; |
| vector<CommandLineFlagInfo> flags; |
| GetAllFlags(&flags); |
| |
| for (const auto& f : flags) { |
| ret_value += "--"; |
| if (mode == EscapeMode::HTML) { |
| ret_value += EscapeForHtmlToString(f.name); |
| } else if (mode == EscapeMode::NONE) { |
| ret_value += f.name; |
| } |
| ret_value += "="; |
| ret_value += CheckFlagAndRedact(f, mode); |
| ret_value += "\n"; |
| } |
| return ret_value; |
| } |
| |
| vector<CommandLineFlagInfo> GetNonDefaultFlagsHelper() { |
| vector<CommandLineFlagInfo> all_flags; |
| GetAllFlags(&all_flags); |
| vector<CommandLineFlagInfo> non_default_flags; |
| std::copy_if(all_flags.begin(), all_flags.end(), std::back_inserter(non_default_flags), |
| [] (const auto& flag) { |
| // In addition to checking that the flag has been rewritten with 'is_default', |
| // we also check that the value is different from the default value. |
| return !flag.is_default && flag.current_value != flag.default_value; |
| }); |
| return non_default_flags; |
| } |
| |
| string GetNonDefaultFlags() { |
| ostringstream args; |
| for (const auto& flag : GetNonDefaultFlagsHelper()) { |
| // Redact the flags tagged as sensitive, if redaction is enabled. |
| string flag_value = CheckFlagAndRedact(flag, EscapeMode::NONE); |
| args << "--" << flag.name << '=' << flag_value << "\n"; |
| } |
| return args.str(); |
| } |
| |
| GFlagsMap GetNonDefaultFlagsMap() { |
| GFlagsMap flags_by_name; |
| for (auto& flag : GetNonDefaultFlagsHelper()) { |
| // Instead of "flags_by_name.emplace(flag.name, std::move(flag))" using |
| // following approach as order of evaluation of parameters could be different |
| // leading to unexpected value in "flag.name". |
| flags_by_name[flag.name] = std::move(flag); |
| } |
| return flags_by_name; |
| } |
| |
| GFlagsMap GetFlagsMap() { |
| vector<CommandLineFlagInfo> default_flags; |
| GetAllFlags(&default_flags); |
| GFlagsMap flags_by_name; |
| for (auto& flag : default_flags) { |
| // See the note in GetNonDefaultFlagsMap() above. |
| flags_by_name[flag.name] = std::move(flag); |
| } |
| return flags_by_name; |
| } |
| |
| Status ParseTriState(const char* flag_name, const std::string& flag_value, |
| TriStateFlag* tri_state) { |
| if (iequals(flag_value, "required")) { |
| *tri_state = TriStateFlag::REQUIRED; |
| } else if (iequals(flag_value, "optional")) { |
| *tri_state = TriStateFlag::OPTIONAL; |
| } else if (iequals(flag_value, "disabled")) { |
| *tri_state = TriStateFlag::DISABLED; |
| } else { |
| return Status::InvalidArgument(strings::Substitute( |
| "$0 flag must be one of 'required', 'optional', or 'disabled'", |
| flag_name)); |
| } |
| return Status::OK(); |
| } |
| |
| // Get the value of an environment variable that has boolean semantics. |
| bool GetBooleanEnvironmentVariable(const char* env_var_name) { |
| const char* const e = getenv(env_var_name); |
| if ((e == nullptr) || |
| (strlen(e) == 0) || |
| (strcasecmp(e, "false") == 0) || |
| (strcasecmp(e, "0") == 0) || |
| (strcasecmp(e, "no") == 0)) { |
| return false; |
| } |
| if ((strcasecmp(e, "true") == 0) || |
| (strcasecmp(e, "1") == 0) || |
| (strcasecmp(e, "yes") == 0)) { |
| return true; |
| } |
| LOG(FATAL) << Substitute("$0: invalid value for environment variable $0", |
| e, env_var_name); |
| return false; // unreachable |
| } |
| |
| } // namespace kudu |