wip test
Some checks failed
Build on RHEL8 / build (push) Failing after 1m15s
Build on RHEL9 / build (push) Successful in 2m58s

This commit is contained in:
2025-07-07 17:36:40 +02:00
parent 9a37cee4e9
commit 318b19ad79
3 changed files with 281 additions and 103 deletions

View File

@ -11,6 +11,16 @@
#include <cstring> #include <cstring>
#include <unistd.h> #include <unistd.h>
ParsedOptions CommandLineOptions::parse(const std::vector<std::string> &args) {
std::vector<char *> argv;
argv.reserve(args.size());
for (const auto &arg : args) {
argv.push_back(const_cast<char *>(arg.c_str()));
}
int argc = static_cast<int>(argv.size());
return parse(argc, argv.data());
}
ParsedOptions CommandLineOptions::parse(int argc, char *argv[]) { ParsedOptions CommandLineOptions::parse(int argc, char *argv[]) {
CommonOptions base; CommonOptions base;
base.port = DEFAULT_TCP_RX_PORTNO; base.port = DEFAULT_TCP_RX_PORTNO;
@ -24,26 +34,26 @@ ParsedOptions CommandLineOptions::parse(int argc, char *argv[]) {
auto optString = buildOptString(); auto optString = buildOptString();
auto longOptions = buildOptionList(); auto longOptions = buildOptionList();
int opt; optind = 0; // reset getopt
int option_index = 0; int opt, option_index = 0;
while ((opt = getopt_long(argc, argv, optString.c_str(), longOptions.data(), while ((opt = getopt_long(argc, argv, optString.c_str(), longOptions.data(),
&option_index)) != -1) { &option_index)) != -1) {
switch (opt) { switch (opt) {
case 'v': case 'v':
case 'h': case 'h':
handleCommonOption(opt, optarg, base);
return base; // exit after version/help
case 'p': case 'p':
case 'u': case 'u':
handleCommonOption(opt, optarg, base); handleCommonOption(opt, optarg, base);
break; break;
case 'c': case 'c':
case 'n': case 'n':
case 't': case 't':
handleAppSpecificOption(opt, optarg, base, multi, frame); handleAppSpecificOption(opt, optarg, base, multi, frame);
break; break;
default: default:
throw sls::RuntimeError("Invalid arguments." + getHelpMessage()); throw sls::RuntimeError("Invalid arguments." + getHelpMessage());
} }
@ -51,38 +61,50 @@ ParsedOptions CommandLineOptions::parse(int argc, char *argv[]) {
// remaining arguments // remaining arguments
if (optind < argc) { if (optind < argc) {
// maintain backward compatibility of [start port] [num receivers]
// [optional arg] ( for multi receiver and frame synchronizer ) // no deprecated arguments
if (appType_ != AppType::SingleReceiver && if (appType_ == AppType::SingleReceiver) {
GetDeprecated(argc, argv, base.port, numReceivers, optionalArg)) {
if (appType_ == AppType::MultiReceiver) {
multi.numReceivers = numReceivers;
multi.callbackEnabled = optionalArg;
} else if (appType_ == AppType::FrameSynchronizer) {
frame.numReceivers = numReceivers;
frame.printHeaders = optionalArg;
}
} else {
throw sls::RuntimeError("Invalid arguments." + getHelpMessage()); throw sls::RuntimeError("Invalid arguments." + getHelpMessage());
} }
// maintain backward compatibility of [start port] [num receivers]
// [optional arg]
if (argc != 3 && argc != 4) {
throw sls::RuntimeError("Invalid number of arguments." +
getHelpMessage());
}
GetDeprecated(argc, argv, base.port, numReceivers, optionalArg);
if (appType_ == AppType::MultiReceiver) {
multi.numReceivers = numReceivers;
multi.callbackEnabled = optionalArg;
} else if (appType_ == AppType::FrameSynchronizer) {
frame.numReceivers = numReceivers;
frame.printHeaders = optionalArg;
}
} }
// Logging
LOG(sls::logINFO) << "Number of receivers: " << numReceivers; LOG(sls::logINFO) << "Number of receivers: " << numReceivers;
LOG(sls::logINFO) << "TCP Port: " << base.port; LOG(sls::logINFO) << "TCP Port: " << base.port;
if (appType_ == AppType::MultiReceiver) {
LOG(sls::logINFO) << "Callback enabled: " << multi.callbackEnabled;
} else if (appType_ == AppType::FrameSynchronizer) {
LOG(sls::logINFO) << "Print headers: " << frame.printHeaders;
}
switch (appType_) { switch (appType_) {
case AppType::SingleReceiver: case AppType::SingleReceiver:
return base; return base;
case AppType::MultiReceiver: case AppType::MultiReceiver:
LOG(sls::logINFO) << "Call back enable: " << multi.callbackEnabled;
static_cast<CommonOptions &>(multi) = base; static_cast<CommonOptions &>(multi) = base;
return multi; return multi;
case AppType::FrameSynchronizer: case AppType::FrameSynchronizer:
LOG(sls::logINFO) << "Print headers: " << frame.printHeaders;
static_cast<CommonOptions &>(frame) = base; static_cast<CommonOptions &>(frame) = base;
return frame; return frame;
default:
throw sls::RuntimeError("Unknown AppType in CommandLineOptions::parse");
} }
throw std::logic_error("Unknown AppType");
} }
std::vector<option> CommandLineOptions::buildOptionList() const { std::vector<option> CommandLineOptions::buildOptionList() const {
@ -122,6 +144,52 @@ std::string CommandLineOptions::buildOptString() const {
return optstr; return optstr;
} }
uint16_t CommandLineOptions::parsePort(const char *optarg) {
uint16_t val = 0;
try {
val = sls::StringTo<uint16_t>(optarg);
} catch (...) {
throw sls::RuntimeError("Could not parse port number " +
std::string(optarg));
}
if (val < 1024) {
throw sls::RuntimeError(
"Invalid/ privileged port number parsed. Min: 1024.");
}
return val;
}
uint16_t CommandLineOptions::parseNumReceivers(const char *optarg) {
uint16_t val = 0;
try {
val = sls::StringTo<uint16_t>(optarg);
} catch (...) {
throw sls::RuntimeError("Could not parse number of receivers " +
std::string(optarg));
}
if (val == 0 || val > MAX_RECEIVERS) {
throw sls::RuntimeError(
"Invalid number of receivers parsed. Options: 1 - " +
std::to_string(MAX_RECEIVERS));
}
return val;
}
uid_t CommandLineOptions::parseUID(const char *optarg) {
uid_t val = -1;
try {
val = sls::StringTo<uid_t>(optarg);
} catch (...) {
throw sls::RuntimeError("Could not parse UID " + std::string(optarg));
}
if (val == static_cast<uid_t>(-1)) {
throw sls::RuntimeError(
"Could not parse UID. Expected a valid user ID." +
std::string(optarg));
}
return val;
}
void CommandLineOptions::handleCommonOption(int opt, const char *optarg, void CommandLineOptions::handleCommonOption(int opt, const char *optarg,
CommonOptions &base) { CommonOptions &base) {
switch (opt) { switch (opt) {
@ -134,21 +202,11 @@ void CommandLineOptions::handleCommonOption(int opt, const char *optarg,
std::cout << getHelpMessage() << std::endl; std::cout << getHelpMessage() << std::endl;
break; break;
case 'p': case 'p':
try { base.port = parsePort(optarg);
base.port = sls::StringTo<uint16_t>(optarg);
} catch (...) {
throw sls::RuntimeError("Invalid port number parsed.");
}
break; break;
case 'u': case 'u':
try { base.userid = parseUID(optarg);
base.userid = sls::StringTo<uid_t>(optarg); setEffectiveUID(base.userid);
if (base.userid != static_cast<uid_t>(-1)) {
setEffectiveUID(base.userid);
}
} catch (...) {
throw sls::RuntimeError("Invalid uid parsed.");
}
break; break;
} }
} }
@ -167,18 +225,7 @@ void CommandLineOptions::handleAppSpecificOption(int opt, const char *optarg,
break; break;
case 'n': { case 'n': {
uint16_t val = 1; auto val = parseNumReceivers(optarg);
try {
val = sls::StringTo<uint16_t>(optarg);
} catch (...) {
throw sls::RuntimeError("Invalid number of receivers parsed." +
std::to_string(val));
}
if (val == 0 || val > MAX_RECEIVERS) {
throw sls::RuntimeError(
"Invalid number of receivers. Options: 1 - " +
std::to_string(MAX_RECEIVERS));
}
if (appType_ == AppType::MultiReceiver) if (appType_ == AppType::MultiReceiver)
multi.numReceivers = val; multi.numReceivers = val;
else if (appType_ == AppType::FrameSynchronizer) else if (appType_ == AppType::FrameSynchronizer)
@ -189,81 +236,55 @@ void CommandLineOptions::handleAppSpecificOption(int opt, const char *optarg,
case 't': case 't':
LOG(sls::logWARNING) << "Deprecated option 't' and '--rx_tcport'. Use " LOG(sls::logWARNING) << "Deprecated option 't' and '--rx_tcport'. Use "
"'p' or '--port' instead."; "'p' or '--port' instead.";
try { base.port = parsePort(optarg);
base.port = sls::StringTo<uint16_t>(optarg);
} catch (...) {
throw sls::RuntimeError("Invalid port number parsed.");
}
break; break;
} }
} }
int CommandLineOptions::GetDeprecated(int argc, char *argv[], void CommandLineOptions::GetDeprecated(int argc, char *argv[],
uint16_t &startPort, uint16_t &startPort,
uint16_t &numReceivers, uint16_t &numReceivers,
bool &optionalArg) { bool &optionalArg) {
std::string deprecatedMessage =
"Detected deprecated Options. Please update.\n";
if (argc > 1) { if (argc > 1) {
try { if (argc == 3 || argc == 4) {
if (argc == 3 || argc == 4) { LOG(sls::logWARNING)
<< "Detected deprecated Options. Please update.\n";
startPort = parsePort(argv[1]);
numReceivers = parseNumReceivers(argv[2]);
if (argc == 4) {
try { try {
startPort = sls::StringTo<uint16_t>(argv[1]); optionalArg = sls::StringTo<bool>(argv[3]);
numReceivers = sls::StringTo<uint16_t>(argv[2]);
} catch (...) { } catch (...) {
throw sls::RuntimeError( throw sls::RuntimeError("Invalid optional argument "
"Invalid port number or number of receivers parsed."); "parsed. Expected 1 (true) or "
"0 (false).");
} }
if (numReceivers > MAX_RECEIVERS) { }
LOG(sls::logERROR) } else
<< "Did you mix up the order of the arguments? Max " throw std::runtime_error("Invalid number of arguments");
"number of recievers: " << MAX_RECEIVERS;
return 0;
}
if (numReceivers == 0) {
LOG(sls::logERROR)
<< "Invalid number of receivers. Options:1 - "
<< MAX_RECEIVERS;
return slsDetectorDefs::FAIL;
}
if (argc == 4) {
try {
optionalArg = sls::StringTo<bool>(argv[3]);
} catch (...) {
throw sls::RuntimeError("Invalid optional argument "
"parsed. Expected 1 (true) or "
"0 (false).");
}
}
} else
throw std::runtime_error("Invalid number of arguments");
} catch (const std::exception &e) {
LOG(sls::logWARNING) << deprecatedMessage;
LOG(sls::logERROR) << e.what();
return 0;
}
} }
return 1;
} }
std::string CommandLineOptions::getTypeString() { std::string CommandLineOptions::getTypeString() const {
switch (appType_) { switch (appType_) {
case AppType::SingleReceiver: case AppType::SingleReceiver:
return "SingleReceiver"; return "slsReceiver";
case AppType::MultiReceiver: case AppType::MultiReceiver:
return "MultiReceiver"; return "slsMultiReceiver";
case AppType::FrameSynchronizer: case AppType::FrameSynchronizer:
return "FrameSynchronizer"; return "slsFrameSynchronizer";
default: default:
return "Unknown"; return "Unknown";
} }
} }
std::string CommandLineOptions::getVersion() { std::string CommandLineOptions::getVersion() const {
return getTypeString() + " Version: " + APIRECEIVER; return getTypeString() + " Version: " + APIRECEIVER;
} }
std::string CommandLineOptions::getHelpMessage() { std::string CommandLineOptions::getHelpMessage() const {
switch (appType_) { switch (appType_) {
case AppType::SingleReceiver: case AppType::SingleReceiver:
return std::string("\nUsage: ") + getTypeString() + " Options:\n" + return std::string("\nUsage: ") + getTypeString() + " Options:\n" +

View File

@ -37,10 +37,11 @@ using ParsedOptions = std::variant<CommonOptions, MultiReceiverOptions, FrameSyn
class CommandLineOptions { class CommandLineOptions {
public: public:
constexpr explicit CommandLineOptions(AppType app) : appType_(app) {} constexpr explicit CommandLineOptions(AppType app) : appType_(app) {}
ParsedOptions parse(const std::vector<std::string> &args); // for testing
ParsedOptions parse(int argc, char *argv[]); ParsedOptions parse(int argc, char *argv[]);
std::string getTypeString(); std::string getTypeString() const;
std::string getVersion(); std::string getVersion() const;
std::string getHelpMessage(); std::string getHelpMessage() const;
static void setupSignalHandler(int signal, void (*handler)(int)); static void setupSignalHandler(int signal, void (*handler)(int));
static void setEffectiveUID(uid_t uid); static void setEffectiveUID(uid_t uid);
@ -49,14 +50,17 @@ class CommandLineOptions {
std::vector<option> buildOptionList() const; std::vector<option> buildOptionList() const;
std::string buildOptString() const; std::string buildOptString() const;
static uint16_t parsePort(const char *optarg);
static uint16_t parseNumReceivers(const char *optarg);
static uid_t parseUID(const char *optarg);
void handleCommonOption(int opt, const char *optarg, CommonOptions &base); void handleCommonOption(int opt, const char *optarg, CommonOptions &base);
void handleAppSpecificOption(int opt, const char *optarg, void handleAppSpecificOption(int opt, const char *optarg,
CommonOptions &base, CommonOptions &base,
MultiReceiverOptions &multi, MultiReceiverOptions &multi,
FrameSyncOptions &frame); FrameSyncOptions &frame);
static int GetDeprecated(int argc, char *argv[], uint16_t &startPort, static void GetDeprecated(int argc, char *argv[], uint16_t &startPort,
uint16_t &numReceivers, bool &optionalArg); uint16_t &numReceivers, bool &optionalArg);
static constexpr uint16_t MAX_RECEIVERS = 1000; static constexpr uint16_t MAX_RECEIVERS = 1000;
}; };

View File

@ -0,0 +1,153 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include "catch.hpp"
#include "CommandLineOptions.h"
#include "sls/versionAPI.h"
namespace sls {
TEST_CASE("CommandLineOption construction", "[.rxcmdcall]") {
CommandLineOptions s(AppType::SingleReceiver);
REQUIRE(s.getTypeString() == "slsReceiver");
REQUIRE(s.getVersion() == std::string("slsReceiver Version: ") + APIRECEIVER);
REQUIRE_NOTHROW(s.getHelpMessage());
CommandLineOptions m(AppType::MultiReceiver);
REQUIRE(m.getTypeString() == "slsMultiReceiver");
REQUIRE(m.getVersion() == std::string("slsMultiReceiver Version: ") + APIRECEIVER);
REQUIRE_NOTHROW(m.getHelpMessage());
CommandLineOptions f(AppType::FrameSynchronizer);
REQUIRE(f.getTypeString() == "slsFrameSynchronizer");
REQUIRE(f.getVersion() == std::string("slsFrameSynchronizer Version: ") + APIRECEIVER);
REQUIRE_NOTHROW(f.getHelpMessage());
}
TEST_CASE("Validate slsReceiver options", "[.rxcmdcall]") {
CommandLineOptions s(AppType::SingleReceiver);
// valid options
REQUIRE_NOTHROW(s.parse({}));
REQUIRE_NOTHROW(s.parse({"", "-v"}));
REQUIRE_NOTHROW(s.parse({"", "-h"}));
REQUIRE_NOTHROW(s.parse({"", "-h", "gdfg"})); // ignored extra args
REQUIRE_NOTHROW(s.parse({"", "-p", "1955"}));
REQUIRE_NOTHROW(s.parse({"", "-u", "1001"}));
REQUIRE_NOTHROW(s.parse({"", "-p", "1234", "-u", "1001"}));
REQUIRE_NOTHROW(s.parse({"", "-t", "1955"}));
// invalid options
REQUIRE_THROWS(s.parse({"", "-c"}));
REQUIRE_THROWS(s.parse({"", "-n", "2"}));
REQUIRE_THROWS(s.parse({"", "-m", "2"}));
}
TEST_CASE("Validate slsMultiReceiver options", "[.rxcmdcall]") {
CommandLineOptions s(AppType::MultiReceiver);
// valid options
REQUIRE_NOTHROW(s.parse({}));
REQUIRE_NOTHROW(s.parse({"", "-v"}));
REQUIRE_NOTHROW(s.parse({"", "-h"}));
REQUIRE_NOTHROW(s.parse({"", "-h", "gdfg"})); // ignored extra args
REQUIRE_NOTHROW(s.parse({"", "-p", "1955"}));
REQUIRE_NOTHROW(s.parse({"", "-u", "1001"}));
REQUIRE_NOTHROW(s.parse({"", "-p", "1234", "-u", "1001"}));
REQUIRE_NOTHROW(s.parse({"", "-c"}));
REQUIRE_NOTHROW(s.parse({"", "-n", "2"}));
REQUIRE_NOTHROW(s.parse({"", "-p", "1234", "-u", "1001", "-c", "-n", "2"}));
// invalid options
REQUIRE_THROWS(s.parse({"", "-t", "1955"}));
REQUIRE_THROWS(s.parse({"", "-m", "2"}));
}
TEST_CASE("Validate slsFrameSynchronizer options", "[.rxcmdcall]") {
CommandLineOptions s(AppType::FrameSynchronizer);
// valid options
REQUIRE_NOTHROW(s.parse({}));
REQUIRE_NOTHROW(s.parse({"", "-v"}));
REQUIRE_NOTHROW(s.parse({"", "-h"}));
REQUIRE_NOTHROW(s.parse({"", "-h", "gdfg"})); // ignored extra args
REQUIRE_NOTHROW(s.parse({"", "-p", "1955"}));
REQUIRE_NOTHROW(s.parse({"", "-u", "1001"}));
REQUIRE_NOTHROW(s.parse({"", "-p", "1234", "-u", "1001"}));
REQUIRE_NOTHROW(s.parse({"", "-c"}));
REQUIRE_NOTHROW(s.parse({"", "-n", "2"}));
REQUIRE_NOTHROW(s.parse({"", "-p", "1234", "-u", "1001", "-c", "-n", "2"}));
// invalid options
REQUIRE_THROWS(s.parse({"", "-t", "1955"}));
REQUIRE_THROWS(s.parse({"", "-m", "2"}));
}
TEST_CASE("Parse version and help", "[.rxcmdcall]") {
CommandLineOptions s(AppType::SingleReceiver);
auto opts = s.parse({});
auto &o = std::get<CommonOptions>(opts);
REQUIRE(o.versionRequested == false);
REQUIRE(o.helpRequested == false);
opts = s.parse({"", "-v"});
o = std::get<CommonOptions>(opts);
REQUIRE(o.versionRequested == true);
REQUIRE(o.helpRequested == false);
opts = s.parse({"", "-h"});
o = std::get<CommonOptions>(opts);
REQUIRE(o.versionRequested == false);
REQUIRE(o.helpRequested == true);
opts = s.parse({"", "-h", "-v"});
o = std::get<CommonOptions>(opts);
REQUIRE(o.versionRequested == false); // because it exits after help
REQUIRE(o.helpRequested == true);
opts = s.parse({"", "-v", "-h"});
o = std::get<CommonOptions>(opts);
REQUIRE(o.helpRequested == false); // because it exits after version
REQUIRE(o.versionRequested == true);
opts = s.parse({"", "-v", "-h", "sdfsf"}); // should ignore extra args
o = std::get<CommonOptions>(opts);
REQUIRE(o.helpRequested == false); // because it exits after version
REQUIRE(o.versionRequested == true);
}
TEST_CASE("Parse port and uid", "[.rxcmdcall]") {
for (auto app : {AppType::SingleReceiver, AppType::MultiReceiver, AppType::FrameSynchronizer}) {
CommandLineOptions s(app);
REQUIRE_THROWS(s.parse({"", "-p", "1234", "-u", "1000"})); // invalid uid
REQUIRE_THROWS(s.parse({"", "-p", "500"})); // invalid port
auto opts = s.parse({"", "-p", "1234", "-u", "1001"});
auto &o = std::get<CommonOptions>(opts);
REQUIRE(o.port == 1234);
REQUIRE(o.userid == 1001);
opts = s.parse({"", "-p", "5678"});
o = std::get<CommonOptions>(opts);
REQUIRE(o.port == 5678);
REQUIRE(o.userid == static_cast<uid_t>(-1)); // default value
opts = s.parse({});
o = std::get<CommonOptions>(opts);
REQUIRE(o.port == 1954); // default value
REQUIRE(o.userid == static_cast<uid_t>(-1)); // default value
}
}
TEST_CASE("Parse number of receivers and optional arg", "[.rxcmdcall]") {
CommandLineOptions s(AppType::MultiReceiver);
// invalid options
REQUIRE_THROWS(s.parse({"", "-n", "0"})); // invalid number of receivers
REQUIRE_THROWS(s.parse({"", "-n", "1001"})); // exceeds max receivers
// valid options
REQUIRE_NOTHROW(s.parse({"", "-n", "10"})); // valid number of receivers
auto opts = s.parse({"", "-n", "5"});
auto &m = std::get<MultiReceiverOptions>(opts);
REQUIRE(m.numReceivers == 5);
REQUIRE(m.callbackEnabled == false); // default value
opts = s.parse({"", "-c", "-n", "3"});
m = std::get<MultiReceiverOptions>(opts);
REQUIRE(m.numReceivers == 3);
REQUIRE(m.callbackEnabled == true);
}
}