made Commadnlineoptions into a class
Some checks failed
Build on RHEL9 / build (push) Failing after 12s
Build on RHEL8 / build (push) Failing after 18s

This commit is contained in:
2025-07-07 14:52:01 +02:00
parent d18ea00b85
commit 9a37cee4e9
5 changed files with 286 additions and 202 deletions

View File

@ -9,263 +9,302 @@
#include <csignal>
#include <cstring>
#include <getopt.h>
#include <unistd.h>
#define MAX_RECEIVERS 1024
ParsedOptions parseCommandLine(AppType app, int argc, char* argv[]) {
ParsedOptions CommandLineOptions::parse(int argc, char *argv[]) {
CommonOptions base;
base.port = DEFAULT_TCP_RX_PORTNO;
MultiReceiverOptions multi;
FrameSyncOptions frame;
uint16_t numReceivers = 1;
bool optionalArg = false;
auto optString = buildOptString();
auto longOptions = buildOptionList();
int opt;
int option_index = 0;
static struct option common_opts[] = {
{"version", no_argument, nullptr, 'v'},
{"port", required_argument, nullptr, 'p'},
{"uid", required_argument, nullptr, 'u'},
{"help", no_argument, nullptr, 'h'},
{nullptr, 0, nullptr, 0}
};
static struct option single_opts[] = {
{"rx_tcpport", required_argument, nullptr, 't'},
{nullptr, 0, nullptr, 0}
};
static struct option multi_opts[] = {
{"callback", no_argument, nullptr, 'c'},
{"rx_tcpport", required_argument, nullptr, 't'},
{"num-receivers", required_argument, nullptr, 'n'},
{nullptr, 0, nullptr, 0}
};
static struct option frame_sync_opts[] = {
{"print-headers", no_argument, nullptr, 'c'},
{"num-receivers", required_argument, nullptr, 'n'},
{nullptr, 0, nullptr, 0}
};
std::vector<option> options;
options.insert(options.end(), std::begin(common_opts), std::end(common_opts) - 1);
if (app == AppType::SingleReceiver) {
options.insert(options.end(), std::begin(single_opts), std::end(single_opts) - 1);
} else if (app == AppType::MultiReceiver) {
options.insert(options.end(), std::begin(multi_opts), std::end(multi_opts) - 1);
} else if (app == AppType::FrameSynchronizer) {
options.insert(options.end(), std::begin(frame_sync_opts), std::end(frame_sync_opts) - 1);
}
std::string optstring = "vp:u:h";
if (app == AppType::SingleReceiver) {
optstring += "t:";
} else if (app == AppType::MultiReceiver) {
optstring += "cn:t:";
} else if (app == AppType::FrameSynchronizer) {
optstring += "cn:";
}
while ((opt = getopt_long(argc, argv, optstring.c_str(), options.data(), &option_index)) != -1) {
while ((opt = getopt_long(argc, argv, optString.c_str(), longOptions.data(),
&option_index)) != -1) {
switch (opt) {
case 'v':
base.versionRequested = true;
std::cout << getVersion(app) << std::endl;
break;
case 'v':
case 'h':
case 'p':
case 'u':
handleCommonOption(opt, optarg, base);
break;
case 'h':
base.helpRequested = true;
std::cout << getHelpMessage(app) << std::endl;
break;
case 'c':
case 'n':
case 't':
handleAppSpecificOption(opt, optarg, base, multi, frame);
break;
case 't':
LOG(sls::logWARNING) << "Deprecated option. Please use 'p' or '--port'.";
[[fallthrough]];
case 'p':
try {
base.port = sls::StringTo<uint16_t>(optarg);
} catch (...) {
throw sls::RuntimeError("Invalid port number parsed.");
}
break;
case 'u':
try {
base.userid = sls::StringTo<uint32_t>(optarg);
if (base.userid != static_cast<uid_t>(-1)) {
setEffectiveUID(base.userid);
}
} catch (...) {
throw sls::RuntimeError("Invalid uid parsed.");
}
break;
case 'n':
try {
if (app == AppType::MultiReceiver)
multi.numReceivers = sls::StringTo<uint16_t>(optarg);
else if (app == AppType::FrameSynchronizer)
frame.numReceivers = sls::StringTo<uint16_t>(optarg);
if (numReceivers == 0 || numReceivers > MAX_RECEIVERS) {
throw sls::RuntimeError("Invalid number of receivers. Max: " + std::to_string(MAX_RECEIVERS));
}
multi.numReceivers = numReceivers;
frame.numReceivers = numReceivers;
} catch (...) {
throw sls::RuntimeError("Invalid number of receivers parsed." + std::to_string(numReceivers));
}
break;
case 'c':
optionalArg = true;
if (app == AppType::MultiReceiver) {
multi.callbackEnabled = true;
} else if (app == AppType::FrameSynchronizer) {
frame.printHeaders = true;
}
break;
default:
throw sls::RuntimeError("Invalid arguments." + getHelpMessage(app));
default:
throw sls::RuntimeError("Invalid arguments." + getHelpMessage());
}
}
// remaining arguments
if (optind < argc) {
// maintain backward compatibility of [start port] [num receivers] [optional arg] ( for multi receiver and frame synchronizer )
if (app != AppType::SingleReceiver && slsDetectorDefs::OK == GetDeprecatedCommandLineOptions(argc, argv, base.port, numReceivers, optionalArg)) {
if (app == AppType::MultiReceiver) {
// maintain backward compatibility of [start port] [num receivers]
// [optional arg] ( for multi receiver and frame synchronizer )
if (appType_ != AppType::SingleReceiver &&
GetDeprecated(argc, argv, base.port, numReceivers, optionalArg)) {
if (appType_ == AppType::MultiReceiver) {
multi.numReceivers = numReceivers;
multi.callbackEnabled = optionalArg;
} else if (app == AppType::FrameSynchronizer) {
} else if (appType_ == AppType::FrameSynchronizer) {
frame.numReceivers = numReceivers;
frame.printHeaders = optionalArg;
}
} else {
throw sls::RuntimeError("Invalid arguments." + getHelpMessage(app));
throw sls::RuntimeError("Invalid arguments." + getHelpMessage());
}
}
LOG(sls::logINFO) << "Number of receivers: " << numReceivers;
LOG(sls::logINFO) << "TCP Port: " << base.port;
switch (app) {
case AppType::SingleReceiver:
return base;
case AppType::MultiReceiver:
LOG(sls::logINFO) << "Call back enable: " << multi.callbackEnabled;
static_cast<CommonOptions&>(multi) = base;
return multi;
case AppType::FrameSynchronizer:
LOG(sls::logINFO) << "Print headers: " << frame.printHeaders;
static_cast<CommonOptions&>(frame) = base;
return frame;
switch (appType_) {
case AppType::SingleReceiver:
return base;
case AppType::MultiReceiver:
LOG(sls::logINFO) << "Call back enable: " << multi.callbackEnabled;
static_cast<CommonOptions &>(multi) = base;
return multi;
case AppType::FrameSynchronizer:
LOG(sls::logINFO) << "Print headers: " << frame.printHeaders;
static_cast<CommonOptions &>(frame) = base;
return frame;
}
throw std::logic_error("Unknown AppType");
}
std::vector<option> CommandLineOptions::buildOptionList() const {
std::vector<option> opts = {
{"version", no_argument, nullptr, 'v'},
{"port", required_argument, nullptr, 'p'},
{"uid", required_argument, nullptr, 'u'},
{"help", no_argument, nullptr, 'h'},
};
int GetDeprecatedCommandLineOptions(int argc, char *argv[], uint16_t &startPort, uint16_t &numReceivers, bool &optionalArg) {
switch (appType_) {
case AppType::SingleReceiver:
opts.push_back({"rx_tcpport", required_argument, nullptr, 't'});
break;
case AppType::MultiReceiver:
opts.push_back({"callback", no_argument, nullptr, 'c'});
opts.push_back({"rx_tcpport", required_argument, nullptr, 't'});
opts.push_back({"num-receivers", required_argument, nullptr, 'n'});
break;
case AppType::FrameSynchronizer:
opts.push_back({"num-receivers", required_argument, nullptr, 'n'});
opts.push_back({"print-headers", no_argument, nullptr, 'c'});
break;
}
opts.push_back({nullptr, 0, nullptr, 0}); // null-terminator for getopt
return opts;
}
std::string CommandLineOptions::buildOptString() const {
std::string optstr = "vp:u:h";
if (appType_ == AppType::MultiReceiver ||
appType_ == AppType::FrameSynchronizer)
optstr += "cn:";
if (appType_ == AppType::SingleReceiver)
optstr += "t:";
return optstr;
}
void CommandLineOptions::handleCommonOption(int opt, const char *optarg,
CommonOptions &base) {
switch (opt) {
case 'v':
base.versionRequested = true;
std::cout << getVersion() << std::endl;
break;
case 'h':
base.helpRequested = true;
std::cout << getHelpMessage() << std::endl;
break;
case 'p':
try {
base.port = sls::StringTo<uint16_t>(optarg);
} catch (...) {
throw sls::RuntimeError("Invalid port number parsed.");
}
break;
case 'u':
try {
base.userid = sls::StringTo<uid_t>(optarg);
if (base.userid != static_cast<uid_t>(-1)) {
setEffectiveUID(base.userid);
}
} catch (...) {
throw sls::RuntimeError("Invalid uid parsed.");
}
break;
}
}
void CommandLineOptions::handleAppSpecificOption(int opt, const char *optarg,
CommonOptions &base,
MultiReceiverOptions &multi,
FrameSyncOptions &frame) {
switch (opt) {
case 'c':
if (appType_ == AppType::MultiReceiver)
multi.callbackEnabled = true;
else if (appType_ == AppType::FrameSynchronizer)
frame.printHeaders = true;
break;
case 'n': {
uint16_t val = 1;
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)
multi.numReceivers = val;
else if (appType_ == AppType::FrameSynchronizer)
frame.numReceivers = val;
break;
}
case 't':
LOG(sls::logWARNING) << "Deprecated option 't' and '--rx_tcport'. Use "
"'p' or '--port' instead.";
try {
base.port = sls::StringTo<uint16_t>(optarg);
} catch (...) {
throw sls::RuntimeError("Invalid port number parsed.");
}
break;
}
}
int CommandLineOptions::GetDeprecated(int argc, char *argv[],
uint16_t &startPort,
uint16_t &numReceivers,
bool &optionalArg) {
std::string deprecatedMessage =
"Detected deprecated Options. Please update.\n";
if (argc > 1) {
try {
if (argc == 3 || argc == 4) {
startPort = sls::StringTo<uint16_t>(argv[1]);
numReceivers = sls::StringTo<uint16_t>(argv[2]);
try {
startPort = sls::StringTo<uint16_t>(argv[1]);
numReceivers = sls::StringTo<uint16_t>(argv[2]);
} catch (...) {
throw sls::RuntimeError(
"Invalid port number or number of receivers parsed.");
}
if (numReceivers > MAX_RECEIVERS) {
LOG(sls::logWARNING) << deprecatedMessage;
LOG(sls::logERROR)
<< "Did you mix up the order of the arguments? Max "
"number of recievers: " << MAX_RECEIVERS;
return slsDetectorDefs::FAIL;
return 0;
}
if (numReceivers == 0) {
LOG(sls::logWARNING) << deprecatedMessage;
LOG(sls::logERROR) << "Invalid number of receivers. Options: 1 - " << MAX_RECEIVERS;
LOG(sls::logERROR)
<< "Invalid number of receivers. Options:1 - "
<< MAX_RECEIVERS;
return slsDetectorDefs::FAIL;
}
if (argc == 4) {
optionalArg = sls::StringTo<bool>(argv[3]);
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 slsDetectorDefs::FAIL;
return 0;
}
}
return slsDetectorDefs::OK;
return 1;
}
void setEffectiveUID(uid_t uid) {
if (geteuid() == uid) {
LOG(sls::logINFO)
<< "Process already has the same Effective UID " << uid;
} else {
if (seteuid(uid) != 0 || geteuid() != uid) {
throw sls::RuntimeError("Could not set Effective UID");
}
LOG(sls::logINFO) << "Process Effective UID changed to " << uid;
std::string CommandLineOptions::getTypeString() {
switch (appType_) {
case AppType::SingleReceiver:
return "SingleReceiver";
case AppType::MultiReceiver:
return "MultiReceiver";
case AppType::FrameSynchronizer:
return "FrameSynchronizer";
default:
return "Unknown";
}
}
std::string getTypeString(const AppType app) {
switch (app) {
case AppType::SingleReceiver: return "SingleReceiver";
case AppType::MultiReceiver: return "MultiReceiver";
case AppType::FrameSynchronizer: return "FrameSynchronizer";
default: return "Unknown";
}
std::string CommandLineOptions::getVersion() {
return getTypeString() + " Version: " + APIRECEIVER;
}
std::string getVersion(AppType app) {
return getTypeString(app) + " Version: " + APIRECEIVER;
}
std::string CommandLineOptions::getHelpMessage() {
switch (appType_) {
case AppType::SingleReceiver:
return std::string("\nUsage: ") + getTypeString() + " Options:\n" +
"\t-v, --version : Version.\n" +
"\t-p, --port : TCP port to communicate with client "
"for "
"configuration. Non-zero and 16 bit.\n" +
"\t-u, --uid : Set effective user id if receiver "
"started "
"with privileges. \n\n";
std::string getHelpMessage(AppType app) {
switch (app) {
case AppType::SingleReceiver:
return std::string("\nUsage: ") + getTypeString(app) + " Options:\n" +
"\t-v, --version : Version.\n" +
"\t-p, --port : TCP port to communicate with client for "
"configuration. Non-zero and 16 bit.\n" +
"\t-u, --uid : Set effective user id if receiver started "
"with privileges. \n\n";
case AppType::MultiReceiver:
return std::string("\nUsage: " + getTypeString() + " Options:\n") +
"\t-v, --version : Version.\n" +
"\t-n, --num-receivers : Number of receivers.\n" +
"\t-p, --port : TCP port to communicate with client "
"for "
"configuration. Non-zero and 16 bit.\n" +
"\t-c, --callback : Enable dummy callbacks for debugging. "
"Disabled by default. \n" +
"\t-u, --uid : Set effective user id if receiver "
"started "
"with privileges. \n\n";
case AppType::MultiReceiver:
return std::string("\nUsage: " + getTypeString(app) + " Options:\n") +
"\t-v, --version : Version.\n" +
"\t-n, --num-receivers : Number of receivers.\n" +
"\t-p, --port : TCP port to communicate with client for "
"configuration. Non-zero and 16 bit.\n" +
"\t-c, --callback : Enable dummy callbacks for debugging. "
"Disabled by default. \n" +
"\t-u, --uid : Set effective user id if receiver started "
"with privileges. \n\n";
case AppType::FrameSynchronizer:
return std::string("\nUsage: " + getTypeString(app) + " Options:\n") +
"\t-v, --version : Version.\n" +
"\t-n, --num-receivers : Number of receivers.\n" +
"\t-p, --port : TCP port to communicate with client for "
"configuration. Non-zero and 16 bit.\n" +
"\t-c, --print-headers : Print callback headers for debugging. "
"Disabled by default.\n" +
"\t-u, --uid : Set effective user id if receiver started "
"with privileges. \n\n";
case AppType::FrameSynchronizer:
return std::string("\nUsage: " + getTypeString() + " Options:\n") +
"\t-v, --version : Version.\n" +
"\t-n, --num-receivers : Number of receivers.\n" +
"\t-p, --port : TCP port to communicate with client "
"for "
"configuration. Non-zero and 16 bit.\n" +
"\t-c, --print-headers : Print callback headers for debugging. "
"Disabled by default.\n" +
"\t-u, --uid : Set effective user id if receiver "
"started "
"with privileges. \n\n";
}
throw sls::RuntimeError("Unknown AppType for help message");
}
void setupSignalHandler(int signal, void (*handler)(int)) {
void CommandLineOptions::setupSignalHandler(int signal, void (*handler)(int)) {
// Catch signal SIGINT to close files and call destructors properly
struct sigaction sa{};
sa.sa_handler = handler;
@ -274,4 +313,16 @@ void setupSignalHandler(int signal, void (*handler)(int)) {
if (sigaction(signal, &sa, nullptr) == -1) {
LOG(sls::logERROR) << "Could not set handler for " << strsignal(signal);
}
}
void CommandLineOptions::setEffectiveUID(uid_t uid) {
if (geteuid() == uid) {
LOG(sls::logINFO) << "Process already has the same Effective UID "
<< uid;
} else {
if (seteuid(uid) != 0 || geteuid() != uid) {
throw sls::RuntimeError("Could not set Effective UID");
}
LOG(sls::logINFO) << "Process Effective UID changed to " << uid;
}
}

View File

@ -3,8 +3,10 @@
#pragma once
#include <cstdint>
#include <getopt.h>
#include <string>
#include <variant>
#include <vector>
enum class AppType {
MultiReceiver,
@ -32,10 +34,29 @@ struct FrameSyncOptions : CommonOptions {
using ParsedOptions = std::variant<CommonOptions, MultiReceiverOptions, FrameSyncOptions>;
ParsedOptions parseCommandLine(AppType app, int argc, char* argv[]);
int GetDeprecatedCommandLineOptions(int argc, char *argv[], uint16_t &startPort, uint16_t &numReceivers, bool &optionalArg);
void setEffectiveUID(uid_t uid);
std::string getTypeString(const AppType app);
std::string getVersion(AppType app);
std::string getHelpMessage(AppType app);
void setupSignalHandler(int signal, void (*handler)(int));
class CommandLineOptions {
public:
constexpr explicit CommandLineOptions(AppType app) : appType_(app) {}
ParsedOptions parse(int argc, char *argv[]);
std::string getTypeString();
std::string getVersion();
std::string getHelpMessage();
static void setupSignalHandler(int signal, void (*handler)(int));
static void setEffectiveUID(uid_t uid);
private:
AppType appType_;
std::vector<option> buildOptionList() const;
std::string buildOptString() const;
void handleCommonOption(int opt, const char *optarg, CommonOptions &base);
void handleAppSpecificOption(int opt, const char *optarg,
CommonOptions &base,
MultiReceiverOptions &multi,
FrameSyncOptions &frame);
static int GetDeprecated(int argc, char *argv[], uint16_t &startPort,
uint16_t &numReceivers, bool &optionalArg);
static constexpr uint16_t MAX_RECEIVERS = 1000;
};

View File

@ -503,7 +503,8 @@ void sigInterruptHandler(int p) {
}
int main(int argc, char *argv[]) {
auto opts = parseCommandLine(AppType::MultiReceiver, argc, argv);
CommandLineOptions cli(AppType::SingleReceiver);
auto opts = cli.parse(argc, argv);
auto &o = std::get<CommonOptions>(opts);
auto &f = std::get<FrameSyncOptions>(opts);
if (o.versionRequested || o.helpRequested) {
@ -512,8 +513,11 @@ int main(int argc, char *argv[]) {
LOG(sls::logINFOBLUE) << "Current Process [ Tid: " << gettid() << ']';
setupSignalHandler(SIGINT, sigInterruptHandler); // close files on ctrl+c
setupSignalHandler(SIGPIPE, SIG_IGN); // handle locally on socket crash
// close files on ctrl+c
CommandLineOptions::setupSignalHandler(SIGINT, sigInterruptHandler);
// handle locally on socket crash
CommandLineOptions::setupSignalHandler(SIGPIPE, SIG_IGN);
semaphores.resize(f.numReceivers);
for (auto &s : semaphores) {
sem_init(&s, 1, 0);

View File

@ -143,7 +143,8 @@ void sigInterruptHandler(int signal) {
int main(int argc, char *argv[]) {
auto opts = parseCommandLine(AppType::MultiReceiver, argc, argv);
CommandLineOptions cli(AppType::SingleReceiver);
auto opts = cli.parse(argc, argv);
auto& o = std::get<CommonOptions>(opts);
auto &m = std::get<MultiReceiverOptions>(opts);
if (o.versionRequested || o.helpRequested) {
@ -152,8 +153,11 @@ int main(int argc, char *argv[]) {
LOG(sls::logINFOBLUE) << "Current Process [ Tid: " << gettid() << ']';
setupSignalHandler(SIGINT, sigInterruptHandler); // close files on ctrl+c
setupSignalHandler(SIGPIPE, SIG_IGN); // handle locally on socket crash
// close files on ctrl+c
CommandLineOptions::setupSignalHandler(SIGINT, sigInterruptHandler);
// handle locally on socket crash
CommandLineOptions::setupSignalHandler(SIGPIPE, SIG_IGN);
sem_init(&semaphore, 1, 0);
/** - loop over receivers */
@ -219,7 +223,7 @@ int main(int argc, char *argv[]) {
/** - Parent process ignores SIGINT and waits for all the child processes to
* handle the signal */
setupSignalHandler(SIGINT, SIG_IGN);
CommandLineOptions::setupSignalHandler(SIGINT, SIG_IGN);
/** - Print Ready and Instructions how to exit */
std::cout << "Ready ... \n";

View File

@ -31,7 +31,8 @@ void sigInterruptHandler(int signal) {
int main(int argc, char *argv[]) {
auto opts = parseCommandLine(AppType::SingleReceiver, argc, argv);
CommandLineOptions cli(AppType::SingleReceiver);
auto opts = cli.parse(argc, argv);
auto& o = std::get<CommonOptions>(opts);
if (o.versionRequested || o.helpRequested) {
return EXIT_SUCCESS;
@ -39,8 +40,11 @@ int main(int argc, char *argv[]) {
LOG(sls::logINFOBLUE) << "Current Process [ Tid: " << gettid() << " ]";
setupSignalHandler(SIGINT, sigInterruptHandler); // close files on ctrl+c
setupSignalHandler(SIGPIPE, SIG_IGN); // handle locally on socket crash
// close files on ctrl+c
CommandLineOptions::setupSignalHandler(SIGINT, sigInterruptHandler);
// handle locally on socket crash
CommandLineOptions::setupSignalHandler(SIGPIPE, SIG_IGN);
sem_init(&semaphore, 1, 0);
try {