2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
42#include "utils/Config.h"
44#include "utils/Daemon.h"
45#include "utils/Exceptions.h"
46#include "utils/Formatter.h"
48#ifndef DOXYGEN_SHOULD_SKIP_THIS
50#include "log/Logger.h"
68#include <unordered_set>
72#pragma GCC diagnostic push
73#pragma GCC diagnostic ignored "-Wfloat-equal"
75#if __has_warning
("-Wweak-vtables")
76#pragma GCC diagnostic ignored "-Wweak-vtables"
78#if __has_warning
("-Wcovered-switch-default")
79#pragma GCC diagnostic ignored "-Wcovered-switch-default"
83#include "utils/CLI11.hpp"
85#pragma GCC diagnostic pop
96 const std::shared_ptr<
CLI::App> app = std::make_shared<
CLI::App>();
98 app->configurable(
false);
100 app->allow_config_extras();
104 helpFormatter->label(
"SUBCOMMAND",
"INSTANCE");
105 helpFormatter->label(
"SUBCOMMANDS",
"INSTANCES");
106 helpFormatter->label(
"PERSISTENT",
"");
107 helpFormatter->label(
"Persistent Options",
"Options (persistent)");
108 helpFormatter->label(
"Nonpersistent Options",
"Options (nonpersistent)");
109 helpFormatter->label(
"Usage",
"\nUsage");
110 helpFormatter->label(
"bool:{true,false}",
"{true,false}");
111 helpFormatter->label(
":{standard,required,full,default}",
"{standard,required,full,default}");
112 helpFormatter->label(
":{standard,exact,expanded}",
"{standard,exact,expanded}");
113 helpFormatter->column_width(7);
115 app->formatter(helpFormatter);
118 app->get_config_formatter_base()->arrayDelimiter(
' ');
120 app->option_defaults()->take_last();
121 app->option_defaults()->group(app->get_formatter()->get_label(
"Nonpersistent Options"));
130 bool Config::init(
int argc,
char* argv[]) {
136 applicationName = std::filesystem::path(argv[0]).filename();
139 struct passwd* pw =
nullptr;
140 struct group* gr =
nullptr;
142 if ((pw = getpwuid(getuid())) ==
nullptr) {
144 }
else if ((gr = getgrgid(pw->pw_gid)) ==
nullptr) {
146 }
else if ((euid = geteuid()) == 0) {
147 configDirectory =
"/etc/snode.c";
148 logDirectory =
"/var/log/snode.c";
149 pidDirectory =
"/var/run/snode.c";
151 const char* homedir =
nullptr;
152 if ((homedir = std::getenv(
"XDG_CONFIG_HOME")) ==
nullptr) {
153 if ((homedir = std::getenv(
"HOME")) ==
nullptr) {
154 homedir = pw->pw_dir;
158 if (homedir !=
nullptr) {
159 configDirectory = std::string(homedir) +
"/.config/snode.c";
160 logDirectory = std::string(homedir) +
"/.local/log/snode.c";
161 pidDirectory = std::string(homedir) +
"/.local/run/snode.c";
167 if (proceed && !std::filesystem::exists(configDirectory)) {
168 if (std::filesystem::create_directories(configDirectory)) {
169 std::filesystem::permissions(
171 (std::filesystem::perms::owner_all | std::filesystem::perms::group_read | std::filesystem::perms::group_exec) &
172 ~std::filesystem::perms::others_all);
173 if (geteuid() == 0) {
174 struct group* gr =
nullptr;
175 if ((gr = getgrnam(
XSTR(GROUP_NAME))) !=
nullptr) {
176 if (chown(configDirectory.c_str(), euid, gr->gr_gid) < 0) {
177 std::cout <<
"Warning: Can not set group ownership of '" << configDirectory
178 <<
"' to 'snodec':" << strerror(errno) << std::endl;
181 std::cout <<
"Error: Can not find group 'snodec'. Add it using groupadd or addgroup" << std::endl;
182 std::cout <<
" and add the current user to this group." << std::endl;
183 std::filesystem::remove(configDirectory);
188 std::cout <<
"Error: Can not create directory '" << configDirectory <<
"'" << std::endl;
193 if (proceed && !std::filesystem::exists(logDirectory)) {
194 if (std::filesystem::create_directories(logDirectory)) {
195 std::filesystem::permissions(logDirectory,
196 (std::filesystem::perms::owner_all | std::filesystem::perms::group_all) &
197 ~std::filesystem::perms::others_all);
198 if (geteuid() == 0) {
199 struct group* gr =
nullptr;
200 if ((gr = getgrnam(
XSTR(GROUP_NAME))) !=
nullptr) {
201 if (chown(logDirectory.c_str(), euid, gr->gr_gid) < 0) {
202 std::cout <<
"Warning: Can not set group ownership of '" << logDirectory <<
"' to 'snodec':" << strerror(errno)
206 std::cout <<
"Error: Can not find group 'snodec'. Add it using groupadd or addgroup" << std::endl;
207 std::cout <<
" and add the current user to this group." << std::endl;
208 std::filesystem::remove(configDirectory);
213 std::cout <<
"Error: Can not create directory '" << logDirectory <<
"'" << std::endl;
218 if (proceed && !std::filesystem::exists(pidDirectory)) {
219 if (std::filesystem::create_directories(pidDirectory)) {
220 std::filesystem::permissions(pidDirectory,
221 (std::filesystem::perms::owner_all | std::filesystem::perms::group_all) &
222 ~std::filesystem::perms::others_all);
223 if (geteuid() == 0) {
224 struct group* gr =
nullptr;
225 if ((gr = getgrnam(
XSTR(GROUP_NAME))) !=
nullptr) {
226 if (chown(pidDirectory.c_str(), euid, gr->gr_gid) < 0) {
227 std::cout <<
"Warning: Can not set group ownership of '" << pidDirectory <<
"' to 'snodec':" << strerror(errno)
231 std::cout <<
"Error: Can not find group 'snodec'. Add it using groupadd or addgroup." << std::endl;
232 std::cout <<
" and add the current user to this group." << std::endl;
233 std::filesystem::remove(configDirectory);
238 std::cout <<
"Error: Can not create directory '" << pidDirectory <<
"'" << std::endl;
244 app->description(
"Configuration for Application '" + applicationName +
"'");
246 app->footer(
"Application '" + applicationName +
247 "' powered by SNode.C\n"
248 "(C) 2020-2025 Volker Christian <me@vchrist.at>\n"
249 "https://github.com/SNodeC/snode.c");
253 configDirectory +
"/" + applicationName +
".conf",
254 "Read a config file",
257 ->type_name(
"configfile")
258 ->check(!
CLI::ExistingDirectory);
260 app->add_option_function<std::string>(
262 []([[maybe_unused]]
const std::string& configFile) {
265 "Write config file and exit")
266 ->configurable(
false)
267 ->default_val(configDirectory +
"/" + applicationName +
".conf")
268 ->type_name(
"[configfile]")
269 ->check(!
CLI::ExistingDirectory)
274 "Kill running daemon")
275 ->configurable(
false)
276 ->disable_flag_override();
279 "-i,--instance-alias",
280 "Make an instance also known as an alias in configuration files")
281 ->configurable(
false)
282 ->type_name(
"instance=instance_alias [instance=instance_alias [...]]")
283 ->each([](
const std::string& item) {
284 const auto it = item.find(
'=');
285 if (it != std::string::npos) {
286 aliases[item.substr(0, it)] = item.substr(it + 1);
288 throw CLI::ConversionError(
"Can not convert '" + item +
"' to a 'instance=instance_alias' pair");
292 addStandardFlags(app.get());
294 logLevelOpt = app->add_option(
299 ->check(
CLI::Range(0, 6))
300 ->group(app->get_formatter()->get_label(
"Persistent Options"));
302 verboseLevelOpt = app->add_option(
303 "-v,--verbose-level",
307 ->check(
CLI::Range(0, 10))
308 ->group(app->get_formatter()->get_label(
"Persistent Options"));
310 quietOpt = app->add_flag(
311 "-q{true},!-u,--quiet{true}",
313 ->default_val(
"false")
315 ->check(
CLI::IsMember({
"true",
"false"}))
316 ->group(app->get_formatter()->get_label(
"Persistent Options"));
318 logFileOpt = app->add_option(
321 ->default_val(logDirectory +
"/" + applicationName +
".log")
322 ->type_name(
"logfile")
323 ->check(!
CLI::ExistingDirectory)
324 ->group(app->get_formatter()->get_label(
"Persistent Options"));
326 enforceLogFileOpt = app->add_flag(
327 "-e{true},!-n,--enforce-log-file{true}",
328 "Enforce writing of logs to file for foreground applications")
329 ->default_val(
"false")
331 ->check(
CLI::IsMember({
"true",
"false"}))
332 ->group(app->get_formatter()->get_label(
"Persistent Options"));
334 daemonizeOpt = app->add_flag(
335 "-d{true},!-f,--daemonize{true}",
336 "Start application as daemon")
337 ->default_val(
"false")
339 ->check(
CLI::IsMember({
"true",
"false"}))
340 ->group(app->get_formatter()->get_label(
"Persistent Options"));
342 userNameOpt = app->add_option(
344 "Run daemon under specific user permissions")
345 ->default_val(pw->pw_name)
346 ->type_name(
"username")
347 ->needs(daemonizeOpt)
348 ->group(app->get_formatter()->get_label(
"Persistent Options"));
350 groupNameOpt = app->add_option(
352 "Run daemon under specific group permissions")
353 ->default_val(gr->gr_name)
354 ->type_name(
"groupname")
355 ->needs(daemonizeOpt)
356 ->group(app->get_formatter()->get_label(
"Persistent Options"));
360 app->set_version_flag(
"--version",
"1.0-rc1",
"Framework version");
367 bool Config::bootstrap() {
370 app->final_callback([]() {
371 if (daemonizeOpt->as<
bool>() && (*app)[
"--show-config"]->count() == 0 && (*app)[
"--write-config"]->count() == 0 &&
372 (*app)[
"--command-line"]->count() == 0) {
373 std::cout <<
"Running as daemon (double fork)" << std::endl;
376 pidDirectory +
"/" + applicationName +
".pid", userNameOpt->as<std::string>()
, groupNameOpt->as<std::string>()
);
380 const std::string logFile = logFileOpt->as<std::string>();
381 if (!logFile.empty()) {
384 }
else if ((*app)[
"--enforce-log-file"]->as<
bool>()) {
385 const std::string logFile = logFileOpt->as<std::string>();
386 if (!logFile.empty()) {
387 std::cout <<
"Writing logs to file " << logFile << std::endl;
397 bool Config::parse1() {
401 app->parse(argc, argv);
402 }
catch (
const CLI::ParseError&) {
406 if ((*app)[
"--kill"]->count() > 0) {
409 std::cout <<
"Daemon terminated: Pid = " << daemonPid << std::endl;
411 std::cout <<
"DaemonError: " << e.what() << std::endl;
413 std::cout <<
"DaemonFailure: " << e.what() << std::endl;
418 if ((*app)[
"--show-config"]->count() == 0 && (*app)[
"--write-config"]->count() == 0 && (*app)[
"--command-line"]->count() == 0) {
419 app->allow_extras(
false);
422 if (!quietOpt->as<
bool>()) {
435 static const std::unordered_set<
char> special{
438 '|',
'&',
';',
'<',
'>',
'(',
')',
'{',
'}',
439 '*',
'?',
'[',
']',
'~',
'!',
'#',
'='
443 out.reserve(s.size() * 2);
446 if (special.contains(c)) {
455 CLI::Option* disabledOpt = app->get_option_no_throw(
"--disabled");
456 const bool disabled = disabledOpt !=
nullptr ? disabledOpt->as<
bool>() :
false;
458 for (
const CLI::Option* option : app->get_options()) {
459 if (option->get_configurable()) {
464 if (option->count() > 0) {
465 value = option->as<std::string>();
466 }
else if (option->get_required()) {
467 value =
"<REQUIRED>";
471 if (option->get_required()) {
472 if (option->count() > 0) {
473 value = option->as<std::string>();
475 value =
"<REQUIRED>";
480 if (option->count() > 0) {
481 value = option->as<std::string>();
482 }
else if (!option->get_default_str().empty()) {
483 value = option->get_default_str();
484 }
else if (!option->get_required()) {
487 value =
"<REQUIRED>";
491 if (!option->get_default_str().empty()) {
492 value = option->get_default_str();
493 }
else if (!option->get_required()) {
496 value =
"<REQUIRED>";
502 if (!value.empty()) {
503 if (value.starts_with(
"[") && value.ends_with(
"]")) {
504 value = value.substr(1, value.size() - 2);
507 if (value !=
"<REQUIRED>" && value !=
"\"\"") {
510 out <<
"--" << option->get_single_name() << ((option->get_items_expected_max() == 0) ?
"=" :
" ") << value <<
" ";
514 }
else if (disabledOpt->get_default_str() ==
"false") {
515 out <<
"--disabled=true ";
520 std::stringstream out;
524 std::string optionString = out.str();
525 if (optionString.back() ==
' ') {
526 optionString.pop_back();
535 std::stringstream out;
537 CLI::Option* disabledOpt = app->get_option_no_throw(
"--disabled");
539 for (
CLI::App* subcommand : app->get_subcommands({})) {
540 if (!subcommand->get_name().empty()) {
550 std::string outString;
553 if (!outString.empty()) {
559 if (!outString.empty()) {
560 out << app->get_name() <<
" " << outString;
565 std::stringstream out;
569 std::string outString = out.str();
570 while (app->get_parent() !=
nullptr) {
571 app = app->get_parent();
574 std::string(app->get_name()).append(
" ").append(!parentOptions.empty() ? parentOptions.append(
" ") :
"").append(outString);
577 if (outString.empty()) {
578 outString = Config::getApplicationName();
584 bool Config::parse2() {
585 bool success =
false;
589 app->parse(argc, argv);
592 std::cout <<
"Daemon error: " << e.what() <<
" ... exiting" << std::endl;
594 std::cout <<
"Daemon failure: " << e.what() <<
" ... exiting" << std::endl;
596 std::cout <<
"Pid: " << getpid() <<
", child pid: " << e
.getPid() <<
": " << e.what() << std::endl;
597 }
catch (
const CLI::CallForHelp&) {
598 const std::string helpMode = helpApp->get_option(
"--help")->as<std::string>();
599 const CLI::App* helpApp =
nullptr;
600 CLI::AppFormatMode mode =
CLI::AppFormatMode::Normal;
601 if (helpMode ==
"exact") {
602 helpApp = utils::Config::helpApp;
603 }
else if (helpMode ==
"expanded") {
604 helpApp = utils::Config::helpApp;
605 mode =
CLI::AppFormatMode::All;
607 std::cout << app->help(helpApp,
"", mode) << std::endl;
608 }
catch (
const CLI::CallForVersion&) {
609 std::cout << app->version() << std::endl << std::endl;
611 std::cout << e.what() << std::endl;
612 std::cout << std::endl
618 std::cout << e
.getApp()->config_to_str(
true,
true);
619 }
catch (
const CLI::ParseError& e1) {
620 std::cout <<
"Error showing config file: " << e
.getApp() <<
" " << e1.get_name() <<
" " << e1.what() << std::endl;
624 std::cout << e.what() << std::endl;
626 if (confFile.is_open()) {
628 confFile << app->config_to_str(
true,
true);
630 }
catch (
const CLI::ParseError& e1) {
632 std::cout <<
"Error writing config file: " << e1.get_name() <<
" " << e1.what() << std::endl;
636 std::cout <<
"Error writing config file: " << std::strerror(errno) << std::endl;
638 }
catch (
const CLI::ConversionError& e) {
641 }
catch (
const CLI::ArgumentMismatch& e) {
644 }
catch (
const CLI::ConfigError& e) {
646 std::cout <<
" Adding '-w' on the command line may solve this problem" << std::endl;
648 }
catch (
const CLI::ParseError& e) {
649 const std::string what = e.what();
650 if (what.find(
"[Option Group: ") != std::string::npos) {
653 <<
" Anonymous instance(s) not configured in source code " << std::endl;
659 }
catch ([[maybe_unused]]
const CLI::ParseError& e) {
660 std::cout << std::endl <<
"Append -h or --help to your command line for more information." << std::endl;
661 }
catch (
const CLI::Error& e) {
663 std::cout <<
"Append -h or --help to your command line for more information." << std::endl;
669 void Config::terminate() {
670 if ((*app)[
"--daemonize"]->as<
bool>()) {
671 std::ifstream pidFile(pidDirectory +
"/" + applicationName +
".pid", std::ifstream::in);
673 if (pidFile.good()) {
677 if (getpid() == pid) {
681 }
else if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) >= 0) {
683 while (read(STDIN_FILENO, buf, 1024) > 0) {
691 sectionFormatter->label(
"SUBCOMMAND",
"SECTION");
692 sectionFormatter->label(
"SUBCOMMANDS",
"SECTIONS");
693 sectionFormatter->label(
"PERSISTENT",
"");
694 sectionFormatter->label(
"Persistent Options",
"Options (persistent)");
695 sectionFormatter->label(
"Nonpersistent Options",
"Options (nonpersistent)");
696 sectionFormatter->label(
"Usage",
"\nUsage");
697 sectionFormatter->label(
"bool:{true,false}",
"{true,false}");
698 sectionFormatter->label(
":{standard,required,full,default}",
"{standard,required,full,default}");
699 sectionFormatter->label(
":{standard,exact,expanded}",
"{standard,exact,expanded}");
700 sectionFormatter->column_width(7);
702 return sectionFormatter;
707 CLI::App* Config::addInstance(
const std::string& name,
const std::string& description,
const std::string& group) {
708 CLI::App* instanceSc = app->add_subcommand(name, description)
711 ->formatter(sectionFormatter)
712 ->configurable(
false)
713 ->allow_extras(
false)
714 ->disabled(name.empty());
718 ->configurable(!instanceSc->get_disabled());
720 if (!instanceSc->get_disabled()) {
721 if (aliases.contains(name)) {
723 ->alias(aliases[name]);
727 utils::Config::addStandardFlags(instanceSc);
728 utils::Config::addHelp(instanceSc);
733 CLI::App* Config::getInstance(
const std::string& name) {
734 return app->get_subcommand(name);
737 CLI::App* Config::addStandardFlags(
CLI::App* app) {
744 "Show current configuration and exit")
745 ->configurable(
false)
746 ->disable_flag_override();
750 "--command-line{standard}",
751 [app]([[maybe_unused]] std::int64_t count) {
752 const std::string& result = app->get_option(
"--command-line")->as<std::string>();
753 if (result ==
"standard") {
756 "Below is a command line viewing all non-default and required options:\n"
757 "* Options show their configured value\n"
758 "* Required but not yet configured options show <REQUIRED> as value\n"
759 "* Options marked as <REQUIRED> need to be configured for a successful bootstrap",
762 if (result ==
"required") {
765 "Below is a command line viewing required options only:\n"
766 "* Options show either their configured or default value\n"
767 "* Required but not yet configured options show <REQUIRED> as value\n"
768 "* Options marked as <REQUIRED> need to be configured for a successful bootstrap",
771 if (result ==
"full") {
774 "Below is a command line viewing the full set of options with their default or configured values:\n"
775 "* Options show either their configured or default value\n"
776 "* Required but not yet configured options show <REQUIRED> as value\n"
777 "* Options marked as <REQUIRED> need to be configured for a successful bootstrap",
780 if (result ==
"default") {
783 "Below is a command line viewing the full set of options with their default values\n"
784 "* Options show their default value\n"
785 "* Required but not yet configured options show <REQUIRED> as value\n"
786 "* Options marked as <REQUIRED> need to be configured for a successful bootstrap",
790 "Print a command line\n"
791 " standard (default): View all non-default and required options\n"
792 " required: View required options only\n"
793 " full: View the full set of options with their default or configured values\n"
794 " default: View the full set of options with their default values")
795 ->configurable(
false)
796 ->check(
CLI::IsMember({
"standard",
"required",
"full",
"default"}));
801 CLI::App* Config::addHelp(
CLI::App* app) {
803 "-h{standard},--help{standard}",
807 "Print help message and exit")
808 ->group(app->get_formatter()->get_label(
"Nonpersistent Options"))
809 ->check(
CLI::IsMember({
"standard",
"exact",
"expanded"}));
814 CLI::App* Config::addSimpleHelp(
CLI::App* app) {
820 "Print help message and exit")
821 ->group(app->get_formatter()->get_label(
"Nonpersistent Options"))
822 ->disable_flag_override();
827 void Config::required(
CLI::App* instance,
bool required) {
829 app->needs(instance);
831 for (
const auto& sub : instance->get_subcommands([](
const CLI::App* sc) ->
bool {
832 return sc->get_required();
834 instance->needs(sub);
837 app->remove_needs(instance);
839 for (
const auto& sub : instance->get_subcommands([](
const CLI::App* sc) ->
bool {
840 return sc->get_required();
842 instance->remove_needs(sub);
846 instance->required(required);
847 instance->ignore_case(required);
850 void Config::disabled(
CLI::App* instance,
bool disabled) {
852 app->remove_needs(instance);
854 for (
const auto& sub : instance->get_subcommands({})) {
857 if (sub->get_ignore_case()) {
858 instance->remove_needs(sub);
859 sub->required(
false);
863 app->needs(instance);
865 for (
const auto& sub : instance->get_subcommands({})) {
868 if (sub->get_ignore_case()) {
869 instance->needs(sub);
875 instance->required(disabled ?
false : instance->get_ignore_case());
878 bool Config::removeInstance(
CLI::App* instance) {
879 Config::required(instance,
false);
881 return app->remove_subcommand(instance);
884 CLI::Option* Config::addStringOption(
const std::string& name,
const std::string& description,
const std::string& typeName) {
885 applicationOptions[name] = app
886 ->add_option(name, description)
887 ->type_name(typeName)
890 ->group(
"Application Options");
892 app->needs(applicationOptions[name]);
894 return applicationOptions[name];
898 Config::addStringOption(
const std::string& name,
const std::string& description,
const std::string& typeName,
bool configurable) {
899 addStringOption(name, description, typeName);
900 return applicationOptions[name]
901 ->configurable(configurable);
904 CLI::Option* Config::addStringOption(
const std::string& name,
905 const std::string& description,
906 const std::string& typeName,
907 const std::string& defaultValue) {
908 addStringOption(name, description, typeName);
910 applicationOptions[name]
912 ->default_str(defaultValue);
914 app->remove_needs(applicationOptions[name]);
916 return applicationOptions[name];
919 CLI::Option* Config::addStringOption(
const std::string& name,
920 const std::string& description,
921 const std::string& typeName,
922 const std::string& defaultValue,
924 addStringOption(name, description, typeName, defaultValue);
925 return applicationOptions[name]
926 ->configurable(configurable);
929 CLI::Option* Config::addStringOption(
const std::string& name,
930 const std::string& description,
931 const std::string& typeName,
932 const char* defaultValue) {
933 return addStringOption(name, description, typeName, std::string(defaultValue));
936 CLI::Option* Config::addStringOption(
937 const std::string& name,
const std::string& description,
const std::string& typeName,
const char* defaultValue,
bool configurable) {
938 return addStringOption(name, description, typeName, std::string(defaultValue), configurable);
941 std::string Config::getStringOptionValue(
const std::string& name) {
942 if (app->get_option(name) ==
nullptr) {
943 throw CLI::OptionNotFound(name);
946 return (*app)[name]->as<std::string>();
949 void Config::addFlag(
const std::string& name,
951 const std::string& description,
954 const std::string& groupName) {
955 app->add_flag(name, variable, description)
957 ->configurable(configurable)
961 std::string Config::getApplicationName() {
962 return applicationName;
965 int Config::getLogLevel() {
966 return logLevelOpt->as<
int>();
969 int Config::getVerboseLevel() {
970 return verboseLevelOpt->as<
int>();
973 int Config::argc = 0;
974 char** Config::argv =
nullptr;
976 std::string Config::applicationName;
978 std::string Config::configDirectory;
979 std::string Config::logDirectory;
980 std::string Config::pidDirectory;
982 CLI::Option* Config::daemonizeOpt =
nullptr;
983 CLI::Option* Config::logFileOpt =
nullptr;
984 CLI::Option* Config::userNameOpt =
nullptr;
985 CLI::Option* Config::groupNameOpt =
nullptr;
986 CLI::Option* Config::enforceLogFileOpt =
nullptr;
987 CLI::Option* Config::logLevelOpt =
nullptr;
988 CLI::Option* Config::verboseLevelOpt =
nullptr;
989 CLI::Option* Config::quietOpt =
nullptr;
991 CLI::App* Config::helpApp =
nullptr;
993 std::map<std::string, std::string> Config::aliases;
994 std::map<std::string,
CLI::Option*> Config::applicationOptions;
CLI::App * getApp() const
CallForCommandline(CLI::App *app, const std::string &description, Mode mode)
CLI::App * getApp() const
CallForShowConfig(CLI::App *app)
std::string getConfigFile() const
CallForWriteConfig(const std::string &configFile)
static void logToFile(const std::string &logFile)
static void setVerboseLevel(int level)
static void setLogLevel(int level)
static void setQuiet(bool quiet=true)
static void startDaemon(const std::string &pidFileName, const std::string &userName, const std::string &groupName)
static pid_t stopDaemon(const std::string &pidFileName)
static void erasePidFile(const std::string &pidFileName)
static std::string createCommandLineTemplate(CLI::App *app, CLI::CallForCommandline::Mode mode)
static void createCommandLineTemplate(std::stringstream &out, CLI::App *app, CLI::CallForCommandline::Mode mode)
static void createCommandLineOptions(std::stringstream &out, CLI::App *app, CLI::CallForCommandline::Mode mode)
static std::shared_ptr< CLI::App > makeApp()
static std::string bash_backslash_escape_no_whitespace(std::string_view s)
static std::string createCommandLineOptions(CLI::App *app, CLI::CallForCommandline::Mode mode)
static std::shared_ptr< CLI::HelpFormatter > makeSectionFormatter()
static std::string createCommandLineSubcommands(CLI::App *app, CLI::CallForCommandline::Mode mode)