Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 74a1803

Browse files
committedNov 11, 2023·
FEAT(server,cli): parse options via CLI11
1 parent 5b60cb4 commit 74a1803

File tree

1 file changed

+188
-145
lines changed

1 file changed

+188
-145
lines changed
 

‎src/murmur/main.cpp

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

19+
#include <boost/optional/optional_io.hpp>
20+
#include <boost/tuple/tuple_io.hpp>
1921
#include <csignal>
2022
#include <iostream>
2123

@@ -46,6 +48,8 @@
4648
# include <sys/syslog.h>
4749
#endif
4850

51+
#include <CLI/CLI.hpp>
52+
4953
QFile *qfLog = nullptr;
5054

5155
static bool bVerbose = false;
@@ -200,6 +204,123 @@ void cleanup(int signum) {
200204
exit(signum);
201205
}
202206

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

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;
280390

281391
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
282392
// For Qt >= 5.10 we use QRandomNumberGenerator that is seeded automatically
@@ -285,154 +395,87 @@ int main(int argc, char **argv) {
285395

286396
qInstallMessageHandler(murmurMessageOutputWithContext);
287397

288-
#ifdef Q_OS_WIN
289-
Tray tray(nullptr, &le);
290-
#endif
398+
CLIOptions cli_options = parseCLI(argc, argv);
291399

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")) {
400+
if (cli_options.quit)
401+
return 0;
402+
403+
if (cli_options.print_license) {
351404
#ifdef Q_OS_WIN
352-
AboutDialog ad(nullptr, AboutDialogOptionsShowLicense);
353-
ad.exec();
354-
return 0;
405+
AboutDialog ad(nullptr, AboutDialogOptionsShowLicense);
406+
ad.exec();
407+
return 0;
355408
#else
356-
qInfo("%s\n", qPrintable(License::license()));
357-
return 0;
409+
qInfo("%s\n", qPrintable(License::license()));
410+
return 0;
358411
#endif
359-
} else if (args.at(i) == QLatin1String("-authors") || args.at(i) == QLatin1String("--authors")) {
412+
} else if (cli_options.print_authors) {
360413
#ifdef Q_OS_WIN
361-
AboutDialog ad(nullptr, AboutDialogOptionsShowAuthors);
362-
ad.exec();
363-
return 0;
414+
AboutDialog ad(nullptr, AboutDialogOptionsShowAuthors);
415+
ad.exec();
416+
return 0;
364417
#else
365-
qInfo("%s\n",
366-
"For a list of authors, please see https://github.com/mumble-voip/mumble/graphs/contributors");
367-
return 0;
418+
qInfo("%s\n", "For a list of authors, please see https://github.com/mumble-voip/mumble/graphs/contributors");
419+
return 0;
368420
#endif
369-
} else if (args.at(i) == QLatin1String("-third-party-licenses")
370-
|| args.at(i) == QLatin1String("--third-party-licenses")) {
421+
} else if (cli_options.print_3rd_party_licenses) {
371422
#ifdef Q_OS_WIN
372-
AboutDialog ad(nullptr, AboutDialogOptionsShowThirdPartyLicenses);
373-
ad.exec();
374-
return 0;
423+
AboutDialog ad(nullptr, AboutDialogOptionsShowThirdPartyLicenses);
424+
ad.exec();
425+
return 0;
375426
#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"
427+
qInfo("%s", qPrintable(License::printableThirdPartyLicenseInfo()));
428+
return 0;
387429
#endif
388-
" -disablesu [srv] Disable password for 'SuperUser' account on server srv.\n"
430+
}
431+
432+
detach = cli_options.cli_detach;
433+
QString inifile = QString::fromStdString(cli_options.ini_file.get_value_or(""));
434+
QString supw;
435+
bool disableSu = false;
436+
bool wipeSsl = cli_options.wipe_ssl;
437+
bool wipeLogs = cli_options.wipe_logs;
438+
int sunum = 1;
389439
#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"
440+
bool readPw = false;
393441
#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-
}
442+
bool logGroups = cli_options.log_groups;
443+
bool logACL = cli_options.log_acls;
444+
445+
bVerbose = cli_options.verbose_logging;
446+
447+
if (cli_options.disable_su_srv.has_value()) {
448+
detach = false;
449+
disableSu = true;
450+
sunum = cli_options.disable_su_srv.get();
434451
}
435452

453+
if (!cli_options.supw_srv.get< 0 >().empty()) {
454+
supw = QString::fromStdString(cli_options.supw_srv.get< 0 >());
455+
sunum = cli_options.supw_srv.get< 1 >().get_value_or(1);
456+
#ifdef Q_OS_LINUX
457+
} else if (cli_options.read_supw_srv.has_value()) {
458+
// Note that it is essential to set detach = false here. If this is ever to be changed, the code part
459+
// handling the readPw = true part has to be moved up so that it is executed before fork is called on Unix
460+
// systems.
461+
462+
detach = false;
463+
readPw = true;
464+
sunum = cli_options.read_supw_srv.get();
465+
}
466+
467+
if (cli_options.limits) {
468+
detach = false;
469+
Meta::mp.read(inifile);
470+
unixhandler.setuid();
471+
unixhandler.finalcap();
472+
LimitTest::testLimits(a);
473+
#endif
474+
}
475+
#ifdef Q_OS_WIN
476+
Tray tray(nullptr, &le);
477+
#endif
478+
436479
if (QSslSocket::supportsSsl()) {
437480
qInfo("SSL: OpenSSL version is '%s'", SSLeay_version(SSLEAY_VERSION));
438481
} else {

0 commit comments

Comments
 (0)
Please sign in to comment.