|
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,133 @@ 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, 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 | + |
189 | 321 | int main(int argc, char **argv) {
|
190 | 322 | // Check for SSE and MMX, but only in the windows binaries
|
191 | 323 | #ifdef Q_OS_WIN
|
@@ -234,180 +366,89 @@ int main(int argc, char **argv) {
|
234 | 366 |
|
235 | 367 | MumbleSSL::initialize();
|
236 | 368 |
|
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 | 369 | qInstallMessageHandler(murmurMessageOutputWithContext);
|
253 | 370 |
|
254 | 371 | #ifdef Q_OS_WIN
|
255 | 372 | Tray tray(nullptr, &le);
|
256 | 373 | #endif
|
| 374 | + CLIOptions cli_options = parseCLI(argc, argv); |
| 375 | + if (cli_options.quit) |
| 376 | + return 0; |
257 | 377 |
|
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) { |
317 | 379 | #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; |
321 | 383 | #else
|
322 |
| - qInfo("%s\n", qPrintable(License::license())); |
323 |
| - return 0; |
| 384 | + qInfo("%s\n", qPrintable(License::license())); |
| 385 | + return 0; |
324 | 386 | #endif
|
325 |
| - } else if (args.at(i) == QLatin1String("-authors") || args.at(i) == QLatin1String("--authors")) { |
| 387 | + } else if (cli_options.print_authors) { |
326 | 388 | #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; |
330 | 392 | #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; |
334 | 395 | #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) { |
337 | 397 | #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; |
370 | 401 | #else
|
371 |
| - " -fg Don't write to the log file.\n" |
| 402 | + qInfo("%s", qPrintable(License::printableThirdPartyLicenseInfo())); |
| 403 | + return 0; |
372 | 404 | #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; |
389 | 416 | #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; |
396 | 418 | #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(); |
409 | 442 | }
|
410 | 443 |
|
| 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 | + |
411 | 452 | if (QSslSocket::supportsSsl()) {
|
412 | 453 | qInfo("SSL: OpenSSL version is '%s'", SSLeay_version(SSLEAY_VERSION));
|
413 | 454 | } else {
|
|
0 commit comments