Skip to content

Commit 2aba211

Browse files
committed
FEAT(server,cli): parse options via CLI11
1 parent 6a8885f commit 2aba211

File tree

1 file changed

+196
-155
lines changed

1 file changed

+196
-155
lines changed

src/murmur/main.cpp

+196-155
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@
3737
# include <sys/syslog.h>
3838
#endif
3939

40+
#include <optional>
41+
#include <tuple>
42+
43+
#include <CLI/CLI.hpp>
44+
4045
extern QFile *qfLog;
4146

4247
static bool bVerbose = false;
@@ -186,6 +191,133 @@ void cleanup(int signum) {
186191
exit(signum);
187192
}
188193

194+
class CLIOptions {
195+
public:
196+
bool quit = false;
197+
std::optional< std::string > ini_file;
198+
std::optional< std::string > db_dump_path;
199+
std::optional< std::string > db_import_path;
200+
std::tuple< std::string, boost::optional< int > > supw_srv;
201+
std::optional< int > disable_su_srv;
202+
bool verbose_logging = false;
203+
bool cli_detach = detach;
204+
bool wipe_ssl = false;
205+
bool wipe_logs = false;
206+
bool log_groups = false;
207+
bool log_acls = false;
208+
209+
bool print_authors = false;
210+
bool print_license = false;
211+
bool print_3rd_party_licenses = false;
212+
213+
#ifdef Q_OS_UNIX
214+
bool limits = false;
215+
std::optional< int > read_supw_srv;
216+
#endif
217+
218+
static constexpr const char *const CLI_ABOUT_SECTION = "About";
219+
static constexpr const char *const CLI_LOGGING_SECTION = "Logging";
220+
static constexpr const char *const CLI_ADMINISTRATION_SECTION = "Administration";
221+
static constexpr const char *const CLI_CONFIGURATION_SECTION = "Configuration";
222+
static constexpr const char *const CLI_TESTING_SECTION = "Testing";
223+
};
224+
225+
CLIOptions parseCLI(int argc, char **argv) {
226+
CLIOptions options;
227+
228+
CLI::App app;
229+
app.set_version_flag("--version", Version::getRelease().toStdString());
230+
231+
app.add_option_no_stream("-i,--ini", options.ini_file, "Specify ini file to use.")
232+
->option_text("<inifile>")
233+
->expected(1, 2)
234+
->check(CLI::ExistingFile)
235+
->group(CLIOptions::CLI_CONFIGURATION_SECTION);
236+
237+
app.add_option("-w,--supw", options.supw_srv, "Set password for 'SuperUser' account on server srv.")
238+
->option_text("<pw> [srv]")
239+
->allow_extra_args()
240+
->expected(0, 1)
241+
->group(CLIOptions::CLI_ADMINISTRATION_SECTION);
242+
243+
#ifdef Q_OS_UNIX
244+
app.add_option_no_stream("-r,--readsupw", options.read_supw_srv,
245+
"Reads password for server srv from standard input.")
246+
->option_text("[srv]")
247+
->default_val(1)
248+
->expected(0, 1)
249+
->group(CLIOptions::CLI_ADMINISTRATION_SECTION);
250+
251+
app.add_flag("-l,--limits", options.limits,
252+
"Tests and shows how many file descriptors and threads can be created.\n"
253+
"The purpose of this option is to test how many clients Mumble server can handle.\n"
254+
"Mumble server will exit after this test.")
255+
->group(CLIOptions::CLI_TESTING_SECTION);
256+
#endif
257+
258+
app.add_option_no_stream("-d,--disablesu", options.disable_su_srv,
259+
"Disable password for 'SuperUser' account on server srv.")
260+
->option_text("[srv]")
261+
->expected(0, 1)
262+
->group(CLIOptions::CLI_ADMINISTRATION_SECTION);
263+
app.add_flag("-s,--wipessl", options.wipe_ssl, "Remove SSL certificates from database.")
264+
->group(CLIOptions::CLI_ADMINISTRATION_SECTION);
265+
app.add_flag("--db-json-dump", options.db_dump_path, "Requests a JSON dump of the database to be written to the given file")
266+
->option_text("[file]")
267+
->expected(0, 1)
268+
->group(CLIOptions::CLI_ADMINISTRATION_SECTION);
269+
app.add_flag("--db-json-import", options.db_import_path, "Reads in the provide JSON file and imports its contents into the database")
270+
->option_text("[file]")
271+
->expected(0, 1)
272+
->group(CLIOptions::CLI_ADMINISTRATION_SECTION);
273+
274+
app.add_flag("-v,--verbose", options.verbose_logging, "Use verbose logging (include debug-logs).")
275+
->group(CLIOptions::CLI_LOGGING_SECTION);
276+
app.add_flag("!-f,!--force-fg", options.cli_detach,
277+
#ifdef Q_OS_UNIX
278+
"Don't detach from console."
279+
#else
280+
"Don't write to the log file."
281+
#endif
282+
)
283+
->group(CLIOptions::CLI_LOGGING_SECTION);
284+
285+
app.add_flag("-p,--wipelogs", options.wipe_logs, "Remove all log entries from database.")
286+
->group(CLIOptions::CLI_LOGGING_SECTION);
287+
app.add_flag("-g,--loggroups", options.log_groups, "Turns on logging for group changes for all servers.")
288+
->group(CLIOptions::CLI_LOGGING_SECTION);
289+
app.add_flag("-a,--logacls", options.log_acls, "Turns on logging for ACL changes for all servers.")
290+
->group(CLIOptions::CLI_LOGGING_SECTION);
291+
292+
293+
app.add_flag("--authors", options.print_authors, "Show Mumble server's authors.")
294+
->group(CLIOptions::CLI_ABOUT_SECTION);
295+
app.add_flag("--license", options.print_license, "Show Mumble server's license.")
296+
->group(CLIOptions::CLI_ABOUT_SECTION);
297+
app.add_flag("--3rd-party-licenses", options.print_3rd_party_licenses,
298+
"Show licenses for third-party software used by Mumble server.")
299+
->group(CLIOptions::CLI_ABOUT_SECTION);
300+
301+
302+
app.footer("If no inifile is provided, Mumble server will search for one in\ndefault locations.");
303+
304+
try {
305+
(app).parse(argc, argv);
306+
} catch (const CLI::ParseError &e) {
307+
std::stringstream info_stream, error_stream;
308+
app.exit(e, info_stream, error_stream);
309+
310+
if (e.get_exit_code() != static_cast< int >(CLI::ExitCodes::Success)) {
311+
qFatal("%s", error_stream.str().c_str());
312+
} else {
313+
qInfo("%s", info_stream.str().c_str());
314+
}
315+
options.quit = true;
316+
}
317+
318+
return options;
319+
}
320+
189321
int main(int argc, char **argv) {
190322
// Check for SSE and MMX, but only in the windows binaries
191323
#ifdef Q_OS_WIN
@@ -234,180 +366,89 @@ int main(int argc, char **argv) {
234366

235367
MumbleSSL::initialize();
236368

237-
QString inifile;
238-
QString supw;
239-
QString dbDumpPath;
240-
QString dbImportPath;
241-
bool disableSu = false;
242-
bool wipeSsl = false;
243-
bool wipeLogs = false;
244-
unsigned int sunum = 0;
245-
#ifdef Q_OS_UNIX
246-
bool readPw = false;
247-
#endif
248-
bool logGroups = false;
249-
bool logACL = false;
250-
251-
252369
qInstallMessageHandler(murmurMessageOutputWithContext);
253370

254371
#ifdef Q_OS_WIN
255372
Tray tray(nullptr, &le);
256373
#endif
374+
CLIOptions cli_options = parseCLI(argc, argv);
375+
if (cli_options.quit)
376+
return 0;
257377

258-
QStringList args = a.arguments();
259-
for (int i = 1; i < args.size(); i++) {
260-
bool bLast = false;
261-
QString arg = args.at(i).toLower();
262-
if ((arg == "-supw")) {
263-
detach = false;
264-
if (i + 1 < args.size()) {
265-
i++;
266-
supw = args.at(i);
267-
if (i + 1 < args.size()) {
268-
i++;
269-
sunum = args.at(i).toUInt();
270-
}
271-
bLast = true;
272-
} else {
273-
#ifdef Q_OS_UNIX
274-
qFatal("-supw expects the password on the command line - maybe you meant -readsupw?");
275-
#else
276-
qFatal("-supw expects the password on the command line");
277-
#endif
278-
}
279-
#ifdef Q_OS_UNIX
280-
} else if ((arg == "-readsupw")) {
281-
// Note that it is essential to set detach = false here. If this is ever to be changed, the code part
282-
// handling the readPw = true part has to be moved up so that it is executed before fork is called on Unix
283-
// systems.
284-
detach = false;
285-
readPw = true;
286-
if (i + 1 < args.size()) {
287-
i++;
288-
sunum = args.at(i).toUInt();
289-
}
290-
bLast = true;
291-
#endif
292-
} else if ((arg == "-disablesu")) {
293-
detach = false;
294-
disableSu = true;
295-
if (i + 1 < args.size()) {
296-
i++;
297-
sunum = args.at(i).toUInt();
298-
}
299-
bLast = true;
300-
} else if ((arg == "-ini") && (i + 1 < args.size())) {
301-
i++;
302-
inifile = args.at(i);
303-
} else if ((arg == "-wipessl")) {
304-
wipeSsl = true;
305-
} else if ((arg == "-wipelogs")) {
306-
wipeLogs = true;
307-
} else if ((arg == "-fg")) {
308-
detach = false;
309-
} else if ((arg == "-v")) {
310-
bVerbose = true;
311-
} else if ((arg == "-version") || (arg == "--version")) {
312-
// Print version and exit (print to regular std::cout to avoid adding any useless meta-information from
313-
// using e.g. qWarning
314-
std::cout << "Mumble server version " << Version::getRelease().toStdString() << std::endl;
315-
return 0;
316-
} else if (args.at(i) == QLatin1String("-license") || args.at(i) == QLatin1String("--license")) {
378+
if (cli_options.print_license) {
317379
#ifdef Q_OS_WIN
318-
AboutDialog ad(nullptr, AboutDialogOptionsShowLicense);
319-
ad.exec();
320-
return 0;
380+
AboutDialog ad(nullptr, AboutDialogOptionsShowLicense);
381+
ad.exec();
382+
return 0;
321383
#else
322-
qInfo("%s\n", qPrintable(License::license()));
323-
return 0;
384+
qInfo("%s\n", qPrintable(License::license()));
385+
return 0;
324386
#endif
325-
} else if (args.at(i) == QLatin1String("-authors") || args.at(i) == QLatin1String("--authors")) {
387+
} else if (cli_options.print_authors) {
326388
#ifdef Q_OS_WIN
327-
AboutDialog ad(nullptr, AboutDialogOptionsShowAuthors);
328-
ad.exec();
329-
return 0;
389+
AboutDialog ad(nullptr, AboutDialogOptionsShowAuthors);
390+
ad.exec();
391+
return 0;
330392
#else
331-
qInfo("%s\n",
332-
"For a list of authors, please see https://github.com/mumble-voip/mumble/graphs/contributors");
333-
return 0;
393+
qInfo("%s\n", "For a list of authors, please see https://github.com/mumble-voip/mumble/graphs/contributors");
394+
return 0;
334395
#endif
335-
} else if (args.at(i) == QLatin1String("-third-party-licenses")
336-
|| args.at(i) == QLatin1String("--third-party-licenses")) {
396+
} else if (cli_options.print_3rd_party_licenses) {
337397
#ifdef Q_OS_WIN
338-
AboutDialog ad(nullptr, AboutDialogOptionsShowThirdPartyLicenses);
339-
ad.exec();
340-
return 0;
341-
#else
342-
qInfo("%s", qPrintable(License::printableThirdPartyLicenseInfo()));
343-
return 0;
344-
#endif
345-
} else if (arg == "--db-json-dump") {
346-
++i;
347-
dbDumpPath = args.at(i);
348-
} else if (arg == "--db-json-import") {
349-
++i;
350-
dbImportPath = args.at(i);
351-
} else if ((arg == "-h") || (arg == "-help") || (arg == "--help")) {
352-
detach = false;
353-
qInfo(
354-
"Usage: %s [-ini <inifile>] [-supw <password>]\n"
355-
" --version Print version information and exit\n"
356-
" -ini <inifile> Specify ini file to use.\n"
357-
" -supw <pw> [srv] Set password for 'SuperUser' account on server srv.\n"
358-
#ifdef Q_OS_UNIX
359-
" -readsupw [srv] Reads password for server srv from standard input.\n"
360-
#endif
361-
" -disablesu [srv] Disable password for 'SuperUser' account on server srv.\n"
362-
#ifdef Q_OS_UNIX
363-
" -limits Tests and shows how many file descriptors and threads can be created.\n"
364-
" The purpose of this option is to test how many clients Murmur can handle.\n"
365-
" Murmur will exit after this test.\n"
366-
#endif
367-
" -v Use verbose logging (include debug-logs).\n"
368-
#ifdef Q_OS_UNIX
369-
" -fg Don't detach from console.\n"
398+
AboutDialog ad(nullptr, AboutDialogOptionsShowThirdPartyLicenses);
399+
ad.exec();
400+
return 0;
370401
#else
371-
" -fg Don't write to the log file.\n"
402+
qInfo("%s", qPrintable(License::printableThirdPartyLicenseInfo()));
403+
return 0;
372404
#endif
373-
" -wipessl Remove SSL certificates from database.\n"
374-
" -wipelogs Remove all log entries from database.\n"
375-
" -loggroups Turns on logging for group changes for all servers.\n"
376-
" -logacls Turns on logging for ACL changes for all servers.\n"
377-
" -version Show version information.\n"
378-
" --db-json-dump [file] Requests a JSON dump of the database to be written to the given file\n"
379-
" --db-json-import [file] Reads in the provide JSON file and imports its contents into the database\n"
380-
"\n"
381-
" -license Show Murmur's license.\n"
382-
" -authors Show Murmur's authors.\n"
383-
" -third-party-licenses Show licenses for third-party software used by Murmur.\n"
384-
"\n"
385-
"If no inifile is provided, murmur will search for one in \n"
386-
"default locations.",
387-
qPrintable(args.at(0)));
388-
return 0;
405+
}
406+
407+
detach = cli_options.cli_detach;
408+
QString inifile = QString::fromStdString(cli_options.ini_file.get_value_or(""));
409+
QString supw;
410+
QString dbDumpPath = QString::fromStdString(cli_options.db_dump_path.get_value_or(""));
411+
QString dbImportPath = QString::fromStdString(cli_options.db_import_path.get_value_or(""));
412+
bool disableSu = false;
413+
bool wipeSsl = cli_options.wipe_ssl;
414+
bool wipeLogs = cli_options.wipe_logs;
415+
unsigned int sunum = 0;
389416
#ifdef Q_OS_UNIX
390-
} else if (arg == "-limits") {
391-
detach = false;
392-
Meta::mp->read(inifile);
393-
unixhandler.setuid();
394-
unixhandler.finalcap();
395-
LimitTest::testLimits(a);
417+
bool readPw = false;
396418
#endif
397-
} else if (arg == "-loggroups") {
398-
logGroups = true;
399-
} else if (arg == "-logacls") {
400-
logACL = true;
401-
} else {
402-
detach = false;
403-
qFatal("Unknown argument %s", qPrintable(args.at(i)));
404-
}
405-
if (bLast && (i + 1 != args.size())) {
406-
detach = false;
407-
qFatal("Password arguments must be last.");
408-
}
419+
bool logGroups = cli_options.log_groups;
420+
bool logACL = cli_options.log_acls;
421+
422+
bVerbose = cli_options.verbose_logging;
423+
424+
if (cli_options.disable_su_srv.has_value()) {
425+
detach = false;
426+
disableSu = true;
427+
sunum = cli_options.disable_su_srv.get();
428+
}
429+
430+
if (!cli_options.supw_srv.get< 0 >().empty()) {
431+
supw = QString::fromStdString(cli_options.supw_srv.get< 0 >());
432+
sunum = cli_options.supw_srv.get< 1 >().get_value_or(1);
433+
#ifdef Q_OS_LINUX
434+
} else if (cli_options.read_supw_srv.has_value()) {
435+
// Note that it is essential to set detach = false here. If this is ever to be changed, the code part
436+
// handling the readPw = true part has to be moved up so that it is executed before fork is called on Unix
437+
// systems.
438+
439+
detach = false;
440+
readPw = true;
441+
sunum = cli_options.read_supw_srv.get();
409442
}
410443

444+
if (cli_options.limits) {
445+
detach = false;
446+
Meta::mp.read(inifile);
447+
unixhandler.setuid();
448+
unixhandler.finalcap();
449+
LimitTest::testLimits(a);
450+
#endif
451+
411452
if (QSslSocket::supportsSsl()) {
412453
qInfo("SSL: OpenSSL version is '%s'", SSLeay_version(SSLEAY_VERSION));
413454
} else {

0 commit comments

Comments
 (0)