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(
"-w,--write-config",
"Write config file and exit")
261 ->configurable(
false)
262 ->default_val(configDirectory +
"/" + applicationName +
".conf")
263 ->type_name(
"[configfile]")
264 ->check(!
CLI::ExistingDirectory)
269 "Kill running daemon")
270 ->configurable(
false)
271 ->disable_flag_override();
274 "-i,--instance-alias",
275 "Make an instance also known as an alias in configuration files")
276 ->configurable(
false)
277 ->type_name(
"instance=instance_alias [instance=instance_alias [...]]")
278 ->each([](
const std::string& item) {
279 const auto it = item.find(
'=');
280 if (it != std::string::npos) {
281 aliases[item.substr(0, it)] = item.substr(it + 1);
283 throw CLI::ConversionError(
"Can not convert '" + item +
"' to a 'instance=instance_alias' pair");
287 addStandardFlags(app.get());
289 logLevelOpt = app->add_option(
294 ->check(
CLI::Range(0, 6))
295 ->group(app->get_formatter()->get_label(
"Persistent Options"));
297 verboseLevelOpt = app->add_option(
298 "-v,--verbose-level",
302 ->check(
CLI::Range(0, 10))
303 ->group(app->get_formatter()->get_label(
"Persistent Options"));
305 quietOpt = app->add_flag(
306 "-q{true},!-u,--quiet{true}",
308 ->default_val(
"false")
310 ->check(
CLI::IsMember({
"true",
"false"}))
311 ->group(app->get_formatter()->get_label(
"Persistent Options"));
313 logFileOpt = app->add_option(
316 ->default_val(logDirectory +
"/" + applicationName +
".log")
317 ->type_name(
"logfile")
318 ->check(!
CLI::ExistingDirectory)
319 ->group(app->get_formatter()->get_label(
"Persistent Options"));
321 enforceLogFileOpt = app->add_flag(
322 "-e{true},!-n,--enforce-log-file{true}",
323 "Enforce writing of logs to file for foreground applications")
324 ->default_val(
"false")
326 ->check(
CLI::IsMember({
"true",
"false"}))
327 ->group(app->get_formatter()->get_label(
"Persistent Options"));
329 daemonizeOpt = app->add_flag(
330 "-d{true},!-f,--daemonize{true}",
331 "Start application as daemon")
332 ->default_val(
"false")
334 ->check(
CLI::IsMember({
"true",
"false"}))
335 ->group(app->get_formatter()->get_label(
"Persistent Options"));
337 userNameOpt = app->add_option(
339 "Run daemon under specific user permissions")
340 ->default_val(pw->pw_name)
341 ->type_name(
"username")
342 ->needs(daemonizeOpt)
343 ->group(app->get_formatter()->get_label(
"Persistent Options"));
345 groupNameOpt = app->add_option(
347 "Run daemon under specific group permissions")
348 ->default_val(gr->gr_name)
349 ->type_name(
"groupname")
350 ->needs(daemonizeOpt)
351 ->group(app->get_formatter()->get_label(
"Persistent Options"));
355 app->set_version_flag(
"--version",
"1.0-rc1",
"Framework version");
362 bool Config::bootstrap() {
365 app->final_callback([]() {
366 if (daemonizeOpt->as<
bool>() && helpTriggerApp ==
nullptr && showConfigTriggerApp ==
nullptr &&
367 (*app)[
"--write-config"]->count() == 0 && (*app)[
"--command-line"]->count() == 0) {
368 std::cout <<
"Running as daemon (double fork)" << std::endl;
371 pidDirectory +
"/" + applicationName +
".pid", userNameOpt->as<std::string>()
, groupNameOpt->as<std::string>()
);
375 const std::string logFile = logFileOpt->as<std::string>();
376 if (!logFile.empty()) {
379 }
else if ((*app)[
"--enforce-log-file"]->as<
bool>()) {
380 const std::string logFile = logFileOpt->as<std::string>();
381 if (!logFile.empty()) {
382 std::cout <<
"Writing logs to file " << logFile << std::endl;
392 bool Config::parse1() {
396 app->parse(argc, argv);
397 }
catch (
const CLI::ParseError&) {
401 if ((*app)[
"--kill"]->count() > 0) {
404 std::cout <<
"Daemon terminated: Pid = " << daemonPid << std::endl;
406 std::cout <<
"DaemonError: " << e.what() << std::endl;
408 std::cout <<
"DaemonFailure: " << e.what() << std::endl;
413 app->allow_extras((*app)[
"--show-config"]->count() != 0);
415 if (!quietOpt->as<
bool>()) {
428 static const std::unordered_set<
char> special{
431 '|',
'&',
';',
'<',
'>',
'(',
')',
'{',
'}',
432 '*',
'?',
'[',
']',
'~',
'!',
'#',
'='
436 out.reserve(s.size() * 3);
438 for (
const char c : s) {
439 if (special.contains(c)) {
448 CLI::Option* disabledOpt = app->get_option_no_throw(
"--disabled");
449 const bool disabled = disabledOpt !=
nullptr ? disabledOpt->as<
bool>() :
false;
451 for (
const CLI::Option* option : app->get_options()) {
452 if (option->get_configurable()) {
457 if (option->count() > 0) {
458 value = option->as<std::string>();
459 }
else if (option->get_required()) {
460 value =
"<REQUIRED>";
464 if (option->get_required()) {
465 if (option->count() > 0) {
466 value = option->as<std::string>();
468 value =
"<REQUIRED>";
473 if (option->count() > 0) {
474 value = option->as<std::string>();
475 }
else if (!option->get_default_str().empty()) {
476 value = option->get_default_str();
477 }
else if (!option->get_required()) {
480 value =
"<REQUIRED>";
484 if (!option->get_default_str().empty()) {
485 value = option->get_default_str();
486 }
else if (!option->get_required()) {
489 value =
"<REQUIRED>";
495 if (!value.empty()) {
496 if (value.starts_with(
"[") && value.ends_with(
"]")) {
497 value = value.substr(1, value.size() - 2);
500 if (value !=
"<REQUIRED>" && value !=
"\"\"") {
503 out <<
"--" << option->get_single_name() << ((option->get_items_expected_max() == 0) ?
"=" :
" ") << value <<
" ";
507 }
else if (disabledOpt->get_default_str() ==
"false") {
508 out <<
"--disabled=true ";
513 std::stringstream out;
517 std::string optionString = out.str();
518 if (!optionString.empty() && optionString.back() ==
' ') {
519 optionString.pop_back();
528 std::stringstream out;
530 CLI::Option* disabledOpt = app->get_option_no_throw(
"--disabled");
532 for (
CLI::App* subcommand : app->get_subcommands({})) {
533 if (!subcommand->get_name().empty()) {
543 std::string outString;
546 if (!outString.empty()) {
552 if (!outString.empty()) {
553 out << app->get_name() <<
" " << outString;
558 std::stringstream out;
562 std::string outString = out.str();
563 while (app->get_parent() !=
nullptr) {
564 app = app->get_parent();
567 std::string(app->get_name()).append(
" ").append(!parentOptions.empty() ? parentOptions.append(
" ") :
"").append(outString);
570 if (outString.empty()) {
571 outString = Config::getApplicationName();
577 bool Config::parse2() {
578 bool success =
false;
583 app->parse(argc, argv);
585 }
catch (
const CLI::ParseError&) {
586 if (helpTriggerApp ==
nullptr) {
587 if (showConfigTriggerApp !=
nullptr) {
591 if ((*app)[
"--write-config"]->count() > 0) {
599 if (helpTriggerApp ==
nullptr) {
600 if (showConfigTriggerApp !=
nullptr) {
604 if ((*app)[
"--write-config"]->count() > 0) {
611 <<
" ... exiting" << std::endl;
614 <<
" ... exiting" << std::endl;
616 std::cout <<
"Pid: " << getpid() <<
", child pid: " << e
.getPid() <<
": " << e.what() << std::endl;
617 }
catch (
const CLI::CallForHelp&) {
618 const std::string helpMode = helpTriggerApp->get_option(
"--help")->as<std::string>();
619 const CLI::App* helpApp =
nullptr;
620 CLI::AppFormatMode mode =
CLI::AppFormatMode::Normal;
621 if (helpMode ==
"exact") {
622 helpApp = utils::Config::helpTriggerApp;
623 }
else if (helpMode ==
"expanded") {
624 helpApp = utils::Config::helpTriggerApp;
625 mode =
CLI::AppFormatMode::All;
627 std::cout << app->help(helpApp,
"", mode) << std::endl;
628 }
catch (
const CLI::CallForVersion&) {
629 std::cout << app->version() << std::endl << std::endl;
631 std::cout << e.what() << std::endl;
632 std::cout << std::endl
638 std::cout << e
.getApp()->config_to_str(
true,
true);
639 }
catch (
const CLI::ParseError& e1) {
641 <<
" " << e1.get_name() <<
" " << e1.what() << std::endl;
645 std::cout << e.what() << std::endl;
647 if (confFile.is_open()) {
649 confFile << app->config_to_str(
true,
true);
651 }
catch (
const CLI::ParseError& e1) {
653 std::cout <<
"Error writing config file: " << e1.get_name() <<
" " << e1.what() << std::endl;
658 <<
"] Writing config file: " << std::strerror(errno) << std::endl;
660 }
catch (
const CLI::ConversionError& e) {
663 }
catch (
const CLI::ArgumentMismatch& e) {
666 }
catch (
const CLI::ConfigError& e) {
668 std::cout <<
" Adding '-w' on the command line may solve this problem" << std::endl;
670 }
catch (
const CLI::ParseError& e) {
671 const std::string what = e.what();
672 if (what.find(
"[Option Group: ") != std::string::npos) {
675 <<
" Anonymous instance(s) not configured in source code " << std::endl;
681 }
catch ([[maybe_unused]]
const CLI::ParseError& e) {
682 std::cout << std::endl <<
"Append -h or --help to your command line for more information." << std::endl;
683 }
catch (
const CLI::Error& e) {
686 std::cout << std::endl <<
"Append -h or --help to your command line for more information." << std::endl;
692 void Config::terminate() {
693 if ((*app)[
"--daemonize"]->as<
bool>()) {
694 std::ifstream pidFile(pidDirectory +
"/" + applicationName +
".pid", std::ifstream::in);
696 if (pidFile.good()) {
700 if (getpid() == pid) {
704 }
else if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) >= 0) {
706 while (read(STDIN_FILENO, buf, 1024) > 0) {
714 sectionFormatter->label(
"SUBCOMMAND",
"SECTION");
715 sectionFormatter->label(
"SUBCOMMANDS",
"SECTIONS");
716 sectionFormatter->label(
"PERSISTENT",
"");
717 sectionFormatter->label(
"Persistent Options",
"Options (persistent)");
718 sectionFormatter->label(
"Nonpersistent Options",
"Options (nonpersistent)");
719 sectionFormatter->label(
"Usage",
"\nUsage");
720 sectionFormatter->label(
"bool:{true,false}",
"{true,false}");
721 sectionFormatter->label(
":{standard,required,full,default}",
"{standard,required,full,default}");
722 sectionFormatter->label(
":{standard,exact,expanded}",
"{standard,exact,expanded}");
723 sectionFormatter->column_width(7);
725 return sectionFormatter;
730 CLI::App* Config::addInstance(
const std::string& name,
const std::string& description,
const std::string& group) {
731 CLI::App* instanceSc = app->add_subcommand(name, description)
734 ->formatter(sectionFormatter)
735 ->configurable(
false)
736 ->allow_extras(
false)
737 ->disabled(name.empty());
741 ->configurable(!instanceSc->get_disabled());
743 if (!instanceSc->get_disabled()) {
744 if (aliases.contains(name)) {
746 ->alias(aliases[name]);
750 utils::Config::addStandardFlags(instanceSc);
751 utils::Config::addHelp(instanceSc);
756 CLI::App* Config::getInstance(
const std::string& name) {
757 return app->get_subcommand(name);
760 CLI::App* Config::addStandardFlags(
CLI::App* app) {
765 showConfigTriggerApp = app;
767 "Show current configuration and exit")
768 ->configurable(
false)
769 ->disable_flag_override();
773 "--command-line{standard}",
774 [app]([[maybe_unused]] std::int64_t count) {
775 const std::string& result = app->get_option(
"--command-line")->as<std::string>();
776 if (result ==
"standard") {
779 "Below is a command line viewing all non-default and required options:\n"
780 "* Options show their configured value\n"
781 "* Required but not yet configured options show <REQUIRED> as value\n"
782 "* Options marked as <REQUIRED> need to be configured for a successful bootstrap",
785 if (result ==
"required") {
788 "Below is a command line viewing required options only:\n"
789 "* Options show either their configured or default value\n"
790 "* Required but not yet configured options show <REQUIRED> as value\n"
791 "* Options marked as <REQUIRED> need to be configured for a successful bootstrap",
794 if (result ==
"full") {
797 "Below is a command line viewing the full set of options with their default or configured values:\n"
798 "* Options show either their configured or default value\n"
799 "* Required but not yet configured options show <REQUIRED> as value\n"
800 "* Options marked as <REQUIRED> need to be configured for a successful bootstrap",
803 if (result ==
"default") {
806 "Below is a command line viewing the full set of options with their default values\n"
807 "* Options show their default value\n"
808 "* Required but not yet configured options show <REQUIRED> as value\n"
809 "* Options marked as <REQUIRED> need to be configured for a successful bootstrap",
813 "Print a command line\n"
814 " standard (default): View all non-default and required options\n"
815 " required: View required options only\n"
816 " full: View the full set of options with their default or configured values\n"
817 " default: View the full set of options with their default values")
818 ->configurable(
false)
819 ->check(
CLI::IsMember({
"standard",
"required",
"full",
"default"}));
824 CLI::App* Config::addHelp(
CLI::App* app) {
826 "-h{exact},--help{exact}",
828 helpTriggerApp = app;
830 "Print help message and exit")
831 ->group(app->get_formatter()->get_label(
"Nonpersistent Options"))
832 ->check(
CLI::IsMember({
"standard",
"exact",
"expanded"}));
837 CLI::App* Config::addSimpleHelp(
CLI::App* app) {
839 "--help{exact},-h{exact}",
841 helpTriggerApp = app;
843 "Print help message and exit")
844 ->group(app->get_formatter()->get_label(
"Nonpersistent Options"))
845 ->check(
CLI::IsMember({
"standard",
"exact"}));
850 void Config::required(
CLI::App* instance,
bool required) {
852 app->needs(instance);
854 for (
const auto& sub : instance->get_subcommands([](
const CLI::App* sc) ->
bool {
855 return sc->get_required();
857 instance->needs(sub);
860 app->remove_needs(instance);
862 for (
const auto& sub : instance->get_subcommands([](
const CLI::App* sc) ->
bool {
863 return sc->get_required();
865 instance->remove_needs(sub);
869 instance->required(required);
870 instance->ignore_case(required);
873 void Config::disabled(
CLI::App* instance,
bool disabled) {
875 if (instance->get_ignore_case()) {
876 app->remove_needs(instance);
879 for (
const auto& sub : instance->get_subcommands({})) {
880 if (sub->get_ignore_case()) {
881 instance->remove_needs(sub);
882 sub->required(
false);
886 if (instance->get_ignore_case()) {
887 app->needs(instance);
890 for (
const auto& sub : instance->get_subcommands({})) {
891 if (sub->get_ignore_case()) {
892 instance->needs(sub);
898 instance->required(disabled ?
false : instance->get_ignore_case());
901 bool Config::removeInstance(
CLI::App* instance) {
902 Config::required(instance,
false);
904 return app->remove_subcommand(instance);
907 CLI::Option* Config::addStringOption(
const std::string& name,
const std::string& description,
const std::string& typeName) {
908 applicationOptions[name] = app
909 ->add_option(name, description)
910 ->type_name(typeName)
913 ->group(
"Application Options");
915 app->needs(applicationOptions[name]);
917 return applicationOptions[name];
921 Config::addStringOption(
const std::string& name,
const std::string& description,
const std::string& typeName,
bool configurable) {
922 addStringOption(name, description, typeName);
923 return applicationOptions[name]
924 ->configurable(configurable);
927 CLI::Option* Config::addStringOption(
const std::string& name,
928 const std::string& description,
929 const std::string& typeName,
930 const std::string& defaultValue) {
931 addStringOption(name, description, typeName);
933 applicationOptions[name]
935 ->default_str(defaultValue);
937 app->remove_needs(applicationOptions[name]);
939 return applicationOptions[name];
942 CLI::Option* Config::addStringOption(
const std::string& name,
943 const std::string& description,
944 const std::string& typeName,
945 const std::string& defaultValue,
947 addStringOption(name, description, typeName, defaultValue);
948 return applicationOptions[name]
949 ->configurable(configurable);
952 CLI::Option* Config::addStringOption(
const std::string& name,
953 const std::string& description,
954 const std::string& typeName,
955 const char* defaultValue) {
956 return addStringOption(name, description, typeName, std::string(defaultValue));
959 CLI::Option* Config::addStringOption(
960 const std::string& name,
const std::string& description,
const std::string& typeName,
const char* defaultValue,
bool configurable) {
961 return addStringOption(name, description, typeName, std::string(defaultValue), configurable);
964 std::string Config::getStringOptionValue(
const std::string& name) {
965 if (app->get_option(name) ==
nullptr) {
966 throw CLI::OptionNotFound(name);
969 return (*app)[name]->as<std::string>();
972 void Config::addFlag(
const std::string& name,
974 const std::string& description,
977 const std::string& groupName) {
978 app->add_flag(name, variable, description)
980 ->configurable(configurable)
984 std::string Config::getApplicationName() {
985 return applicationName;
988 int Config::getLogLevel() {
989 return logLevelOpt->as<
int>();
992 int Config::getVerboseLevel() {
993 return verboseLevelOpt->as<
int>();
996 int Config::argc = 0;
997 char** Config::argv =
nullptr;
999 std::string Config::applicationName;
1001 std::string Config::configDirectory;
1002 std::string Config::logDirectory;
1003 std::string Config::pidDirectory;
1005 CLI::Option* Config::daemonizeOpt =
nullptr;
1006 CLI::Option* Config::logFileOpt =
nullptr;
1007 CLI::Option* Config::userNameOpt =
nullptr;
1008 CLI::Option* Config::groupNameOpt =
nullptr;
1009 CLI::Option* Config::enforceLogFileOpt =
nullptr;
1010 CLI::Option* Config::logLevelOpt =
nullptr;
1011 CLI::Option* Config::verboseLevelOpt =
nullptr;
1012 CLI::Option* Config::quietOpt =
nullptr;
1014 CLI::App* Config::helpTriggerApp =
nullptr;
1015 CLI::App* Config::showConfigTriggerApp =
nullptr;
1017 std::map<std::string, std::string> Config::aliases;
1018 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)