37
37
# include < sys/syslog.h>
38
38
#endif
39
39
40
+ #include < optional>
41
+ #include < tuple>
42
+
43
+ #include < CLI/CLI.hpp>
44
+
40
45
extern QFile *qfLog;
41
46
42
47
static bool bVerbose = false ;
@@ -186,6 +191,135 @@ void cleanup(int signum) {
186
191
exit (signum);
187
192
}
188
193
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, std::optional< unsigned int > > supw_srv;
201
+ std::optional< unsigned 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< unsigned 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 ,
266
+ " Requests a JSON dump of the database to be written to the given file" )
267
+ ->option_text (" [file]" )
268
+ ->expected (0 , 1 )
269
+ ->group (CLIOptions::CLI_ADMINISTRATION_SECTION);
270
+ app.add_flag (" --db-json-import" , options.db_import_path ,
271
+ " Reads in the provide JSON file and imports its contents into the database" )
272
+ ->option_text (" [file]" )
273
+ ->expected (0 , 1 )
274
+ ->group (CLIOptions::CLI_ADMINISTRATION_SECTION);
275
+
276
+ app.add_flag (" -v,--verbose" , options.verbose_logging , " Use verbose logging (include debug-logs)." )
277
+ ->group (CLIOptions::CLI_LOGGING_SECTION);
278
+ app.add_flag (" !-f,!--force-fg" , options.cli_detach ,
279
+ #ifdef Q_OS_UNIX
280
+ " Don't detach from console."
281
+ #else
282
+ " Don't write to the log file."
283
+ #endif
284
+ )
285
+ ->group (CLIOptions::CLI_LOGGING_SECTION);
286
+
287
+ app.add_flag (" -p,--wipelogs" , options.wipe_logs , " Remove all log entries from database." )
288
+ ->group (CLIOptions::CLI_LOGGING_SECTION);
289
+ app.add_flag (" -g,--loggroups" , options.log_groups , " Turns on logging for group changes for all servers." )
290
+ ->group (CLIOptions::CLI_LOGGING_SECTION);
291
+ app.add_flag (" -a,--logacls" , options.log_acls , " Turns on logging for ACL changes for all servers." )
292
+ ->group (CLIOptions::CLI_LOGGING_SECTION);
293
+
294
+
295
+ app.add_flag (" --authors" , options.print_authors , " Show Mumble server's authors." )
296
+ ->group (CLIOptions::CLI_ABOUT_SECTION);
297
+ app.add_flag (" --license" , options.print_license , " Show Mumble server's license." )
298
+ ->group (CLIOptions::CLI_ABOUT_SECTION);
299
+ app.add_flag (" --3rd-party-licenses" , options.print_3rd_party_licenses ,
300
+ " Show licenses for third-party software used by Mumble server." )
301
+ ->group (CLIOptions::CLI_ABOUT_SECTION);
302
+
303
+
304
+ app.footer (" If no inifile is provided, Mumble server will search for one in\n default locations." );
305
+
306
+ try {
307
+ (app).parse (argc, argv);
308
+ } catch (const CLI::ParseError &e) {
309
+ std::stringstream info_stream, error_stream;
310
+ app.exit (e, info_stream, error_stream);
311
+
312
+ if (e.get_exit_code () != static_cast < int >(CLI::ExitCodes::Success)) {
313
+ qFatal (" %s" , error_stream.str ().c_str ());
314
+ } else {
315
+ qInfo (" %s" , info_stream.str ().c_str ());
316
+ }
317
+ options.quit = true ;
318
+ }
319
+
320
+ return options;
321
+ }
322
+
189
323
int main (int argc, char **argv) {
190
324
// Check for SSE and MMX, but only in the windows binaries
191
325
#ifdef Q_OS_WIN
@@ -234,178 +368,86 @@ int main(int argc, char **argv) {
234
368
235
369
MumbleSSL::initialize ();
236
370
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
-
252
371
qInstallMessageHandler (murmurMessageOutputWithContext);
253
372
254
373
#ifdef Q_OS_WIN
255
374
Tray tray (nullptr , &le);
256
375
#endif
376
+ CLIOptions cli_options = parseCLI (argc, argv);
377
+ if (cli_options.quit )
378
+ return 0 ;
257
379
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" )) {
380
+ if (cli_options.print_license ) {
317
381
#ifdef Q_OS_WIN
318
- AboutDialog ad (nullptr , AboutDialogOptionsShowLicense);
319
- ad.exec ();
320
- return 0 ;
382
+ AboutDialog ad (nullptr , AboutDialogOptionsShowLicense);
383
+ ad.exec ();
384
+ return 0 ;
321
385
#else
322
- qInfo (" %s\n " , qPrintable (License::license ()));
323
- return 0 ;
386
+ qInfo (" %s\n " , qPrintable (License::license ()));
387
+ return 0 ;
324
388
#endif
325
- } else if (args. at (i) == QLatin1String ( " -authors " ) || args. at (i) == QLatin1String ( " --authors " ) ) {
389
+ } else if (cli_options. print_authors ) {
326
390
#ifdef Q_OS_WIN
327
- AboutDialog ad (nullptr , AboutDialogOptionsShowAuthors);
328
- ad.exec ();
329
- return 0 ;
391
+ AboutDialog ad (nullptr , AboutDialogOptionsShowAuthors);
392
+ ad.exec ();
393
+ return 0 ;
330
394
#else
331
- qInfo (" %s\n " ,
332
- " For a list of authors, please see https://github.com/mumble-voip/mumble/graphs/contributors" );
333
- return 0 ;
395
+ qInfo (" %s\n " , " For a list of authors, please see https://github.com/mumble-voip/mumble/graphs/contributors" );
396
+ return 0 ;
334
397
#endif
335
- } else if (args.at (i) == QLatin1String (" -third-party-licenses" )
336
- || args.at (i) == QLatin1String (" --third-party-licenses" )) {
398
+ } else if (cli_options.print_3rd_party_licenses ) {
337
399
#ifdef Q_OS_WIN
338
- AboutDialog ad (nullptr , AboutDialogOptionsShowThirdPartyLicenses);
339
- ad.exec ();
340
- return 0 ;
400
+ AboutDialog ad (nullptr , AboutDialogOptionsShowThirdPartyLicenses);
401
+ ad.exec ();
402
+ return 0 ;
341
403
#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 "
404
+ qInfo (" %s" , qPrintable (License::printableThirdPartyLicenseInfo ()));
405
+ return 0 ;
366
406
#endif
367
- " -v Use verbose logging (include debug-logs).\n "
407
+ }
408
+
409
+ detach = cli_options.cli_detach ;
410
+ QString inifile = QString::fromStdString (cli_options.ini_file .value_or (" " ));
411
+ QString supw;
412
+ bool disableSu = false ;
413
+ bool wipeSsl = cli_options.wipe_ssl ;
414
+ bool wipeLogs = cli_options.wipe_logs ;
415
+ unsigned int sunum = 0 ;
368
416
#ifdef Q_OS_UNIX
369
- " -fg Don't detach from console.\n "
370
- #else
371
- " -fg Don't write to the log file.\n "
417
+ bool readPw = false ;
372
418
#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 ;
389
- #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);
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 ) {
425
+ detach = false ;
426
+ disableSu = true ;
427
+ sunum = *cli_options.disable_su_srv ;
428
+ }
429
+
430
+ if (!std::get< 0 >(cli_options.supw_srv ).empty ()) {
431
+ supw = QString::fromStdString (std::get< 0 >(cli_options.supw_srv ));
432
+ sunum = std::get< 1 >(cli_options.supw_srv ).value_or < unsigned int >(1 );
433
+ #ifdef Q_OS_LINUX
434
+ } else if (cli_options.read_supw_srv ) {
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 ;
442
+ }
443
+
444
+ if (cli_options.limits ) {
445
+ detach = false ;
446
+ Meta::mp->read (inifile);
447
+ unixhandler.setuid ();
448
+ unixhandler.finalcap ();
449
+ LimitTest::testLimits (a);
396
450
#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
- }
409
451
}
410
452
411
453
if (QSslSocket::supportsSsl ()) {
@@ -420,22 +462,22 @@ int main(int argc, char **argv) {
420
462
421
463
Meta::mp->read (inifile);
422
464
423
- if (!dbDumpPath. isEmpty () ) {
465
+ if (cli_options. db_dump_path ) {
424
466
DBWrapper wrapper (Meta::getConnectionParameter ());
425
467
426
- std::ofstream file (dbDumpPath. toStdString () );
468
+ std::ofstream file (*cli_options. db_dump_path );
427
469
file << wrapper.exportDBToJSON ().dump (2 );
428
470
429
- qInfo (" Dumped JSON representation of database contents to '%s'" , qPrintable (dbDumpPath ));
471
+ qInfo (" Dumped JSON representation of database contents to '%s'" , cli_options. db_dump_path -> c_str ( ));
430
472
431
473
return 0 ;
432
474
}
433
475
434
- if (!dbImportPath. isEmpty () ) {
435
- qInfo (" Importing contents of '%s' into database" , qPrintable (dbImportPath ));
476
+ if (cli_options. db_import_path ) {
477
+ qInfo (" Importing contents of '%s' into database" , cli_options. db_import_path -> c_str ( ));
436
478
DBWrapper wrapper (Meta::getConnectionParameter ());
437
479
438
- std::ifstream file (dbImportPath. toStdString () );
480
+ std::ifstream file (*cli_options. db_import_path );
439
481
440
482
nlohmann::json json;
441
483
file >> json;
0 commit comments