Skip to content

Commit 19349a6

Browse files
committed
FEAT(server,cli): parse options via CLI11
1 parent f6e4840 commit 19349a6

File tree

1 file changed

+181
-145
lines changed

1 file changed

+181
-145
lines changed

src/murmur/main.cpp

+181-145
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
#include "ServerDB.h"
1717
#include "Version.h"
1818

19+
#include <boost/logic/tribool.hpp>
20+
#include <boost/optional/optional_io.hpp>
21+
#include <boost/tuple/tuple_io.hpp>
1922
#include <csignal>
2023
#include <iostream>
2124

@@ -46,6 +49,8 @@
4649
# include <sys/syslog.h>
4750
#endif
4851

52+
#include <CLI/CLI.hpp>
53+
4954
QFile *qfLog = nullptr;
5055

5156
static bool bVerbose = false;
@@ -200,6 +205,114 @@ void cleanup(int signum) {
200205
exit(signum);
201206
}
202207

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

269-
QString inifile;
270-
QString supw;
271-
bool disableSu = false;
272-
bool wipeSsl = false;
273-
bool wipeLogs = false;
274-
int sunum = 1;
275-
#ifdef Q_OS_UNIX
276-
bool readPw = false;
277-
#endif
278-
bool logGroups = false;
279-
bool logACL = false;
280382

281383
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
282384
// For Qt >= 5.10 we use QRandomNumberGenerator that is seeded automatically
@@ -285,153 +387,87 @@ int main(int argc, char **argv) {
285387

286388
qInstallMessageHandler(murmurMessageOutputWithContext);
287389

288-
#ifdef Q_OS_WIN
289-
Tray tray(nullptr, &le);
290-
#endif
390+
auto cli_options = parseCLI(argc, argv);
291391

292-
QStringList args = a.arguments();
293-
for (int i = 1; i < args.size(); i++) {
294-
bool bLast = false;
295-
QString arg = args.at(i).toLower();
296-
if ((arg == "-supw")) {
297-
detach = false;
298-
if (i + 1 < args.size()) {
299-
i++;
300-
supw = args.at(i);
301-
if (i + 1 < args.size()) {
302-
i++;
303-
sunum = args.at(i).toInt();
304-
}
305-
bLast = true;
306-
} else {
307-
#ifdef Q_OS_UNIX
308-
qFatal("-supw expects the password on the command line - maybe you meant -readsupw?");
309-
#else
310-
qFatal("-supw expects the password on the command line");
311-
#endif
312-
}
313-
#ifdef Q_OS_UNIX
314-
} else if ((arg == "-readsupw")) {
315-
// Note that it is essential to set detach = false here. If this is ever to be changed, the code part
316-
// handling the readPw = true part has to be moved up so that it is executed before fork is called on Unix
317-
// systems.
318-
detach = false;
319-
readPw = true;
320-
if (i + 1 < args.size()) {
321-
i++;
322-
sunum = args.at(i).toInt();
323-
}
324-
bLast = true;
325-
#endif
326-
} else if ((arg == "-disablesu")) {
327-
detach = false;
328-
disableSu = true;
329-
if (i + 1 < args.size()) {
330-
i++;
331-
sunum = args.at(i).toInt();
332-
}
333-
bLast = true;
334-
} else if ((arg == "-ini") && (i + 1 < args.size())) {
335-
i++;
336-
inifile = args.at(i);
337-
} else if ((arg == "-wipessl")) {
338-
wipeSsl = true;
339-
} else if ((arg == "-wipelogs")) {
340-
wipeLogs = true;
341-
} else if ((arg == "-fg")) {
342-
detach = false;
343-
} else if ((arg == "-v")) {
344-
bVerbose = true;
345-
} else if ((arg == "-version") || (arg == "--version")) {
346-
// Print version and exit (print to regular std::cout to avoid adding any useless meta-information from
347-
// using e.g. qWarning
348-
std::cout << "Mumble server version " << Version::getRelease().toStdString() << std::endl;
349-
return 0;
350-
} else if (args.at(i) == QLatin1String("-license") || args.at(i) == QLatin1String("--license")) {
392+
if (cli_options.quit)
393+
return 0;
394+
395+
if (cli_options.print_license) {
351396
#ifdef Q_OS_WIN
352-
AboutDialog ad(nullptr, AboutDialogOptionsShowLicense);
353-
ad.exec();
354-
return 0;
397+
AboutDialog ad(nullptr, AboutDialogOptionsShowLicense);
398+
ad.exec();
399+
return 0;
355400
#else
356-
qInfo("%s\n", qPrintable(License::license()));
357-
return 0;
401+
qInfo("%s\n", qPrintable(License::license()));
402+
return 0;
403+
bool readPw = false;
358404
#endif
359-
} else if (args.at(i) == QLatin1String("-authors") || args.at(i) == QLatin1String("--authors")) {
405+
} else if (cli_options.print_authors) {
360406
#ifdef Q_OS_WIN
361-
AboutDialog ad(nullptr, AboutDialogOptionsShowAuthors);
362-
ad.exec();
363-
return 0;
407+
AboutDialog ad(nullptr, AboutDialogOptionsShowAuthors);
408+
ad.exec();
409+
return 0;
364410
#else
365-
qInfo("%s\n",
366-
"For a list of authors, please see https://github.com/mumble-voip/mumble/graphs/contributors");
367-
return 0;
411+
qInfo("%s\n", "For a list of authors, please see https://github.com/mumble-voip/mumble/graphs/contributors");
412+
return 0;
368413
#endif
369-
} else if (args.at(i) == QLatin1String("-third-party-licenses")
370-
|| args.at(i) == QLatin1String("--third-party-licenses")) {
414+
} else if (cli_options.print_3rd_party_licenses) {
371415
#ifdef Q_OS_WIN
372-
AboutDialog ad(nullptr, AboutDialogOptionsShowThirdPartyLicenses);
373-
ad.exec();
374-
return 0;
416+
AboutDialog ad(nullptr, AboutDialogOptionsShowThirdPartyLicenses);
417+
ad.exec();
418+
return 0;
375419
#else
376-
qInfo("%s", qPrintable(License::printableThirdPartyLicenseInfo()));
377-
return 0;
378-
#endif
379-
} else if ((arg == "-h") || (arg == "-help") || (arg == "--help")) {
380-
detach = false;
381-
qInfo("Usage: %s [-ini <inifile>] [-supw <password>]\n"
382-
" --version Print version information and exit\n"
383-
" -ini <inifile> Specify ini file to use.\n"
384-
" -supw <pw> [srv] Set password for 'SuperUser' account on server srv.\n"
385-
#ifdef Q_OS_UNIX
386-
" -readsupw [srv] Reads password for server srv from standard input.\n"
420+
qInfo("%s", qPrintable(License::printableThirdPartyLicenseInfo()));
421+
return 0;
387422
#endif
388-
" -disablesu [srv] Disable password for 'SuperUser' account on server srv.\n"
423+
}
424+
425+
detach = boost::logic::indeterminate(cli_options.detach) ? detach : static_cast< bool >(cli_options.detach);
426+
QString inifile = QString::fromStdString(cli_options.ini_file.get_value_or(""));
427+
QString supw;
428+
bool disableSu = false;
429+
bool wipeSsl = cli_options.wipe_ssl;
430+
bool wipeLogs = cli_options.wipe_logs;
431+
int sunum = 1;
389432
#ifdef Q_OS_UNIX
390-
" -limits Tests and shows how many file descriptors and threads can be created.\n"
391-
" The purpose of this option is to test how many clients Murmur can handle.\n"
392-
" Murmur will exit after this test.\n"
433+
bool readPw = false;
434+
#endif
435+
bool logGroups = cli_options.log_groups;
436+
bool logACL = cli_options.log_acls;
437+
438+
bVerbose = cli_options.verbose_logging;
439+
440+
if (cli_options.disable_su_srv.has_value()) {
441+
detach = false;
442+
disableSu = true;
443+
sunum = cli_options.disable_su_srv.get();
444+
}
445+
446+
if (!cli_options.supw_srv.get< 0 >().empty()) {
447+
supw = QString::fromStdString(cli_options.supw_srv.get< 0 >());
448+
sunum = cli_options.supw_srv.get< 1 >().get_value_or(1);
449+
#ifdef Q_OS_LINUX
450+
} else if (cli_options.read_supw_srv.has_value()) {
451+
// Note that it is essential to set detach = false here. If this is ever to be changed, the code part
452+
// handling the readPw = true part has to be moved up so that it is executed before fork is called on Unix
453+
// systems.
454+
455+
detach = false;
456+
readPw = true;
457+
sunum = cli_options.read_supw_srv.get();
458+
}
459+
460+
if (cli_options.limits) {
461+
detach = false;
462+
Meta::mp.read(inifile);
463+
unixhandler.setuid();
464+
unixhandler.finalcap();
465+
LimitTest::testLimits(a);
393466
#endif
394-
" -v Use verbose logging (include debug-logs).\n"
395-
#ifdef Q_OS_UNIX
396-
" -fg Don't detach from console.\n"
397-
#else
398-
" -fg Don't write to the log file.\n"
399-
#endif
400-
" -wipessl Remove SSL certificates from database.\n"
401-
" -wipelogs Remove all log entries from database.\n"
402-
" -loggroups Turns on logging for group changes for all servers.\n"
403-
" -logacls Turns on logging for ACL changes for all servers.\n"
404-
" -version Show version information.\n"
405-
"\n"
406-
" -license Show Murmur's license.\n"
407-
" -authors Show Murmur's authors.\n"
408-
" -third-party-licenses Show licenses for third-party software used by Murmur.\n"
409-
"\n"
410-
"If no inifile is provided, murmur will search for one in \n"
411-
"default locations.",
412-
qPrintable(args.at(0)));
413-
return 0;
414-
#ifdef Q_OS_UNIX
415-
} else if (arg == "-limits") {
416-
detach = false;
417-
Meta::mp.read(inifile);
418-
unixhandler.setuid();
419-
unixhandler.finalcap();
420-
LimitTest::testLimits(a);
421-
#endif
422-
} else if (arg == "-loggroups") {
423-
logGroups = true;
424-
} else if (arg == "-logacls") {
425-
logACL = true;
426-
} else {
427-
detach = false;
428-
qFatal("Unknown argument %s", qPrintable(args.at(i)));
429-
}
430-
if (bLast && (i + 1 != args.size())) {
431-
detach = false;
432-
qFatal("Password arguments must be last.");
433-
}
434467
}
468+
#ifdef Q_OS_WIN
469+
Tray tray(nullptr, &le);
470+
#endif
435471

436472
if (QSslSocket::supportsSsl()) {
437473
qInfo("SSL: OpenSSL version is '%s'", SSLeay_version(SSLEAY_VERSION));

0 commit comments

Comments
 (0)