proper clean up and versioning of command line arguments for receiver and multi receiver

This commit is contained in:
maliakal_d 2025-03-21 00:27:40 +01:00
parent 89726ab3ff
commit 7844216812
4 changed files with 261 additions and 202 deletions

View File

@ -13,22 +13,12 @@ class Receiver : private virtual slsDetectorDefs {
public: public:
/** /**
* Constructor * Constructor
* Starts up a Receiver server. Reads configuration file, options, and * Starts up a Receiver server.
* assembles a Receiver using TCP and UDP detector interfaces * Assembles a Receiver using TCP and UDP detector interfaces
* throws an exception in case of failure * throws an exception in case of failure
* @param argc from command line * @param port TCP/IP port number
* @param argv from command line
*/ */
Receiver(int argc, char *argv[]); Receiver(uint16_t port = 1954);
/**
* Constructor
* Starts up a Receiver server. Reads configuration file, options, and
* assembles a Receiver using TCP and UDP detector interfaces
* throws an exception in case of failure
* @param tcpip_port_no TCP/IP port number
*/
Receiver(uint16_t tcpip_port_no = 1954);
~Receiver(); ~Receiver();

View File

@ -7,10 +7,12 @@
#include "sls/container_utils.h" #include "sls/container_utils.h"
#include "sls/logger.h" #include "sls/logger.h"
#include "sls/sls_detector_defs.h" #include "sls/sls_detector_defs.h"
#include "sls/sls_detector_exceptions.h"
#include "sls/versionAPI.h" #include "sls/versionAPI.h"
#include <csignal> //SIGINT #include <csignal> //SIGINT
#include <cstring> #include <cstring>
#include <getopt.h>
#include <iostream> #include <iostream>
#include <semaphore.h> #include <semaphore.h>
#include <sys/wait.h> //wait #include <sys/wait.h> //wait
@ -29,28 +31,6 @@
sem_t semaphore; sem_t semaphore;
/**
* Control+C Interrupt Handler
* to let all the processes know to exit properly
*/
void sigInterruptHandler(int p) { sem_post(&semaphore); }
/**
* prints usage of this example program
*/
std::string getHelpMessage() {
std::ostringstream os;
os << "\nUsage:\n\n"
<< "./slsMultiReceiver --version or -v\n"
<< "\t - Gets the slsMultiReceiver version\n\n"
<< "./slsMultiReceiver [start tcp port] [num recevers] [call back "
"option (optional)]\n"
<< "\t - tcp port has to be non-zero and 16 bit\n"
<< "\t - call back option is 0 (disabled) by default, 1 prints frame "
"header for debugging\n";
return os.str();
}
/** /**
* Start Acquisition Call back (slsMultiReceiver writes data if file write * Start Acquisition Call back (slsMultiReceiver writes data if file write
* enabled) if registerCallBackRawDataReady or * enabled) if registerCallBackRawDataReady or
@ -153,6 +133,60 @@ void GetData(slsDetectorDefs::sls_receiver_header &header,
imageSize = 26000; imageSize = 26000;
} }
/**
* Control+C Interrupt Handler
* to let all the processes know to exit properly
*/
void sigInterruptHandler(int p) { sem_post(&semaphore); }
void GetDeprecatedCommandLineOptions(int argc, char *argv[],
uint16_t &startPort,
uint16_t &numReceivers,
bool &callbackEnabled) {
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]);
if (numReceivers > 1024) {
LOG(sls::logWARNING) << deprecatedMessage;
LOG(sls::logERROR)
<< "Did you mix up the order of the arguments?";
exit(EXIT_FAILURE);
}
if (numReceivers == 0) {
LOG(sls::logWARNING) << deprecatedMessage;
LOG(sls::logERROR) << "Invalid number of receivers.";
exit(EXIT_FAILURE);
}
if (argc == 4) {
callbackEnabled = sls::StringTo<bool>(argv[3]);
}
} else
throw std::runtime_error("Invalid number of arguments");
} catch (const std::exception &e) {
LOG(sls::logWARNING) << deprecatedMessage;
LOG(sls::logERROR) << e.what();
exit(EXIT_FAILURE);
}
}
}
std::string getHelpMessage() {
std::string name = "slsMultiReceiver";
return "\nUsage: " + name + " Options:\n" +
"\t-v, --version : Version of " + name + ".\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. Disabled (0) by "
"default. Prints frame header for debugging.\n" +
"\t-u, --uid : Set effective user id if receiver started "
"with privileges. \n\n";
}
/** /**
* Example of main program using the Receiver class * Example of main program using the Receiver class
* *
@ -161,60 +195,109 @@ void GetData(slsDetectorDefs::sls_receiver_header &header,
* - Default Start TCP port is 1954 * - Default Start TCP port is 1954
*/ */
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
uint16_t startPort = DEFAULT_TCP_RX_PORTNO;
uint16_t numReceivers = 1;
bool callbackEnabled = false;
uid_t userid = -1;
// version std::string help_message = getHelpMessage();
if (argc == 2) {
std::string sargv1 = std::string(argv[1]); static struct option long_options[] = {
if (sargv1 == "--version" || sargv1 == "-v") { {"help", no_argument, nullptr, 'h'},
std::cout << "slsMultiReceiver Version: " << APIRECEIVER {"version", no_argument, nullptr, 'v'},
<< std::endl; {"num-receivers", required_argument, nullptr, 'n'},
{"rx_tcpport", required_argument, nullptr, 't'},
{"port", required_argument, nullptr, 'p'},
{"callback", required_argument, nullptr, 'c'},
{"uid", required_argument, nullptr, 'u'},
{nullptr, 0, nullptr, 0}};
int option_index = 0;
int opt = 0;
while (-1 != (opt = getopt_long(argc, argv, "hvn:t:p:u:c:", long_options,
&option_index))) {
switch (opt) {
case 'n':
try {
numReceivers = sls::StringTo<uint16_t>(optarg);
if (numReceivers == 0 || numReceivers > 100) {
throw std::runtime_error("Invalid argument.");
}
} catch (...) {
throw sls::RuntimeError("Invalid number of receivers." +
help_message);
}
break;
case 't':
LOG(sls::logWARNING)
<< "Deprecated option. Please use 'p' or '--port'.";
case 'p':
try {
startPort = sls::StringTo<uint16_t>(optarg);
} catch (...) {
throw sls::RuntimeError("Could not scan port number." +
help_message);
}
break;
case 'c':
try {
callbackEnabled = sls::StringTo<bool>(optarg);
} catch (...) {
throw sls::RuntimeError("Invalid callback enable." +
help_message);
}
break;
case 'u':
try {
userid = sls::StringTo<uint32_t>(optarg);
} catch (...) {
throw sls::RuntimeError("Invalid uid." + help_message);
}
break;
case 'h':
std::cout << help_message << std::endl;
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
default:
// maintain backward compatibility of [startport] [num-receivers]
// [callback]
GetDeprecatedCommandLineOptions(argc, argv, startPort, numReceivers,
callbackEnabled);
throw sls::RuntimeError(help_message);
} }
} }
/** - set default values */ LOG(sls::logINFOBLUE) << "Current Process [ Tid: " << gettid() << ']';
int numReceivers = 1; LOG(sls::logINFO) << "Number of Receivers: " << numReceivers;
uint16_t startTCPPort = DEFAULT_TCP_RX_PORTNO; LOG(sls::logINFO) << "Start TCP Port: " << startPort;
int withCallback = 0; LOG(sls::logINFO) << "Callback Enable: " << callbackEnabled;
sem_init(&semaphore, 1, 0);
/** - get number of receivers and start tcp port from command line // set effective id if provided
* arguments */ if (userid != static_cast<uid_t>(-1)) {
if (argc > 1) { if (geteuid() == userid) {
try { LOG(sls::logINFO)
if (argc == 3 || argc == 4) { << "Process already has the same Effective UID " << userid;
startTCPPort = sls::StringTo<uint16_t>(argv[1]); } else {
if (startTCPPort == 0) { if (seteuid(userid) != 0) {
throw std::runtime_error("Invalid start tcp port"); std::ostringstream oss;
} oss << "Could not set Effective UID to " << userid;
numReceivers = std::stoi(argv[2]); throw sls::RuntimeError(oss.str());
if (numReceivers > 1024) { }
cprintf(RED, if (geteuid() != userid) {
"Did you mix up the order of the arguments?\n%s\n", std::ostringstream oss;
getHelpMessage().c_str()); oss << "Could not set Effective UID to " << userid << ". Got "
return EXIT_FAILURE; << geteuid();
} throw sls::RuntimeError(oss.str());
if (numReceivers == 0) { }
cprintf(RED, "Invalid number of receivers.\n%s\n", LOG(sls::logINFO) << "Process Effective UID changed to " << userid;
getHelpMessage().c_str());
return EXIT_FAILURE;
}
if (argc == 4) {
withCallback = std::stoi(argv[3]);
}
} else
throw std::runtime_error("Invalid number of arguments");
} catch (const std::exception &e) {
cprintf(RED, "Error: %s\n%s\n", e.what(), getHelpMessage().c_str());
return EXIT_FAILURE;
} }
} }
cprintf(BLUE, "Parent Process Created [ Tid: %ld ]\n", (long)gettid());
cprintf(RESET, "Number of Receivers: %d\n", numReceivers);
cprintf(RESET, "Start TCP Port: %hu\n", startTCPPort);
cprintf(RESET, "Callback Enable: %d\n", withCallback);
/** - Catch signal SIGINT to close files and call destructors properly */ /** - Catch signal SIGINT to close files and call destructors properly */
struct sigaction sa; struct sigaction sa;
sa.sa_flags = 0; // no flags sa.sa_flags = 0; // no flags
@ -222,7 +305,7 @@ int main(int argc, char *argv[]) {
sigemptyset(&sa.sa_mask); // dont block additional signals during invocation sigemptyset(&sa.sa_mask); // dont block additional signals during invocation
// of handler // of handler
if (sigaction(SIGINT, &sa, nullptr) == -1) { if (sigaction(SIGINT, &sa, nullptr) == -1) {
cprintf(RED, "Could not set handler function for SIGINT\n"); LOG(sls::logERROR) << "Could not set handler function for SIGINT";
} }
/** - Ignore SIG_PIPE, prevents global signal handler, handle locally, /** - Ignore SIG_PIPE, prevents global signal handler, handle locally,
@ -234,10 +317,11 @@ int main(int argc, char *argv[]) {
sigemptyset(&asa.sa_mask); // dont block additional signals during sigemptyset(&asa.sa_mask); // dont block additional signals during
// invocation of handler // invocation of handler
if (sigaction(SIGPIPE, &asa, nullptr) == -1) { if (sigaction(SIGPIPE, &asa, nullptr) == -1) {
cprintf(RED, "Could not set handler function for SIGPIPE\n"); LOG(sls::logERROR) << "Could not set handler function for SIGPIPE";
} }
/** - loop over number of receivers */ /** - loop over number of receivers */
sem_init(&semaphore, 1, 0);
for (int i = 0; i < numReceivers; ++i) { for (int i = 0; i < numReceivers; ++i) {
/** - fork process to create child process */ /** - fork process to create child process */
@ -246,35 +330,38 @@ int main(int argc, char *argv[]) {
/** - if fork failed, raise SIGINT and properly destroy all child /** - if fork failed, raise SIGINT and properly destroy all child
* processes */ * processes */
if (pid < 0) { if (pid < 0) {
cprintf(RED, "fork() failed. Killing all the receiver objects\n"); LOG(sls::logERROR)
<< "fork() failed. Killing all the receiver objects";
raise(SIGINT); raise(SIGINT);
} }
/** - if child process */ /** - if child process */
else if (pid == 0) { else if (pid == 0) {
cprintf(BLUE, "Child process %d [ Tid: %ld ]\n", i, (long)gettid()); LOG(sls::logINFOBLUE)
<< "Child process " << i << " [ Tid: " << gettid() << ']';
try { try {
uint16_t port = startTCPPort + i; uint16_t port = startPort + i;
sls::Receiver receiver(port); sls::Receiver receiver(port);
/** - register callbacks. remember to set file write enable /** - register callbacks. remember to set file write enable
* to 0 (using the client) if we should not write files and you * to 0 (using the client) if we should not write files and you
* will write data using the callbacks */ * will write data using the callbacks */
if (withCallback) { if (callbackEnabled) {
/** - Call back for start acquisition */ /** - Call back for start acquisition */
cprintf(BLUE, "Registering StartAcq()\n"); LOG(sls::logINFOBLUE) << "Registering StartAcq()";
receiver.registerCallBackStartAcquisition(StartAcq, receiver.registerCallBackStartAcquisition(StartAcq,
nullptr); nullptr);
/** - Call back for acquisition finished */ /** - Call back for acquisition finished */
cprintf(BLUE, "Registering AcquisitionFinished()\n"); LOG(sls::logINFOBLUE)
<< "Registering AcquisitionFinished()";
receiver.registerCallBackAcquisitionFinished( receiver.registerCallBackAcquisitionFinished(
AcquisitionFinished, nullptr); AcquisitionFinished, nullptr);
/* - Call back for raw data */ /* - Call back for raw data */
cprintf(BLUE, "Registering GetData() \n"); LOG(sls::logINFOBLUE) << "Registering GetData()";
receiver.registerCallBackRawDataReady(GetData, nullptr); receiver.registerCallBackRawDataReady(GetData, nullptr);
} }
@ -288,8 +375,8 @@ int main(int argc, char *argv[]) {
throw; throw;
} }
cprintf(BLUE, "Exiting Child Process [ Tid: %ld ]\n", LOG(sls::logINFOBLUE)
(long)gettid()); << "Exiting Child Process [ Tid: " << gettid() << ']';
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
} }
@ -301,12 +388,12 @@ int main(int argc, char *argv[]) {
sigemptyset(&sa.sa_mask); // dont block additional signals during invocation sigemptyset(&sa.sa_mask); // dont block additional signals during invocation
// of handler // of handler
if (sigaction(SIGINT, &sa, nullptr) == -1) { if (sigaction(SIGINT, &sa, nullptr) == -1) {
cprintf(RED, "Could not set handler function for SIGINT\n"); LOG(sls::logERROR) << "Could not set handler function for SIGINT";
} }
/** - Print Ready and Instructions how to exit */ /** - Print Ready and Instructions how to exit */
std::cout << "Ready ... \n"; std::cout << "Ready ... \n";
cprintf(RESET, "\n[ Press \'Ctrl+c\' to exit ]\n"); LOG(sls::logINFO) << "\n[ Press \'Ctrl+c\' to exit ]";
/** - Parent process waits for all child processes to exit */ /** - Parent process waits for all child processes to exit */
for (;;) { for (;;) {
@ -315,18 +402,19 @@ int main(int argc, char *argv[]) {
// no child closed // no child closed
if (childPid == -1) { if (childPid == -1) {
if (errno == ECHILD) { if (errno == ECHILD) {
cprintf(GREEN, "All Child Processes have been closed\n"); LOG(sls::logINFOGREEN)
<< "All Child Processes have been closed";
break; break;
} else { } else {
cprintf(RED, "Unexpected error from waitpid(): (%s)\n", LOG(sls::logERROR)
strerror(errno)); << "Unexpected error from waitpid(): " << strerror(errno);
break; break;
} }
} }
// child closed // child closed
cprintf(BLUE, "Exiting Child Process [ Tid: %ld ]\n", LOG(sls::logINFOBLUE)
(long int)childPid); << "Exiting Child Process [ Tid: " << childPid << ']';
} }
std::cout << "Goodbye!\n"; std::cout << "Goodbye!\n";

View File

@ -27,106 +27,9 @@ namespace sls {
Receiver::~Receiver() = default; Receiver::~Receiver() = default;
Receiver::Receiver(int argc, char *argv[]) : tcpipInterface(nullptr) { Receiver::Receiver(uint16_t port) {
validatePortNumber(port);
// options tcpipInterface = make_unique<ClientInterface>(port);
uint16_t tcpip_port_no = 1954;
uid_t userid = -1;
// parse command line for config
static struct option long_options[] = {
// These options set a flag.
//{"verbose", no_argument, &verbose_flag, 1},
// These options dont set a flag. We distinguish them by their indices.
{"rx_tcpport", required_argument, nullptr,
't'}, // TODO change or backward compatible to "port, p"?
{"uid", required_argument, nullptr, 'u'},
{"version", no_argument, nullptr, 'v'},
{"help", no_argument, nullptr, 'h'},
{nullptr, 0, nullptr, 0}};
// initialize global optind variable (required when instantiating multiple
// receivers in the same process)
optind = 1;
// getopt_long stores the option index here.
int option_index = 0;
int c = 0;
std::string help_message =
"\nUsage: " + std::string(argv[0]) + " [arguments]\n" +
"Possible arguments are:\n" +
"\t-t, --rx_tcpport <port> : TCP Communication Port with "
"client. Non-zero and 16 bit.\n" +
"\t-u, --uid <user id> : Set effective user id if receiver "
"\n" +
"\t started with privileges. \n\n";
while (c != -1) {
c = getopt_long(argc, argv, "hvt:u:", long_options, &option_index);
// Detect the end of the options.
if (c == -1)
break;
switch (c) {
case 't':
try {
tcpip_port_no = sls::StringTo<uint16_t>(optarg);
validatePortNumber(tcpip_port_no);
} catch (...) {
throw RuntimeError("Could not scan TCP port number." +
help_message);
}
break;
case 'u':
if (sscanf(optarg, "%u", &userid) != 1) {
throw RuntimeError("Could not scan uid" + help_message);
}
break;
case 'v':
std::cout << "slsReceiver Version: " << APIRECEIVER << std::endl;
LOG(logINFOBLUE) << "Exiting [ Tid: " << gettid() << " ]";
exit(EXIT_SUCCESS);
case 'h':
std::cout << help_message << std::endl;
exit(EXIT_SUCCESS);
default:
throw RuntimeError(help_message);
}
}
// set effective id if provided
if (userid != static_cast<uid_t>(-1)) {
if (geteuid() == userid) {
LOG(logINFO) << "Process already has the same Effective UID "
<< userid;
} else {
if (seteuid(userid) != 0) {
std::ostringstream oss;
oss << "Could not set Effective UID to " << userid;
throw RuntimeError(oss.str());
}
if (geteuid() != userid) {
std::ostringstream oss;
oss << "Could not set Effective UID to " << userid << ". Got "
<< geteuid();
throw RuntimeError(oss.str());
}
LOG(logINFO) << "Process Effective UID changed to " << userid;
}
}
// might throw an exception
tcpipInterface = make_unique<ClientInterface>(tcpip_port_no);
}
Receiver::Receiver(uint16_t tcpip_port_no) {
// might throw an exception
tcpipInterface = make_unique<ClientInterface>(tcpip_port_no);
} }
std::string Receiver::getReceiverVersion() { std::string Receiver::getReceiverVersion() {

View File

@ -2,11 +2,13 @@
// Copyright (C) 2021 Contributors to the SLS Detector Package // Copyright (C) 2021 Contributors to the SLS Detector Package
/* slsReceiver */ /* slsReceiver */
#include "sls/Receiver.h" #include "sls/Receiver.h"
#include "sls/ToString.h"
#include "sls/container_utils.h" #include "sls/container_utils.h"
#include "sls/logger.h" #include "sls/logger.h"
#include "sls/sls_detector_defs.h" #include "sls/sls_detector_defs.h"
#include <csignal> //SIGINT #include <csignal> //SIGINT
#include <getopt.h>
#include <semaphore.h> #include <semaphore.h>
#include <unistd.h> #include <unistd.h>
@ -22,9 +24,84 @@ void sigInterruptHandler(int p) { sem_post(&semaphore); }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
sem_init(&semaphore, 1, 0); uint16_t port = DEFAULT_TCP_RX_PORTNO;
uid_t userid = -1;
LOG(sls::logINFOBLUE) << "Created [ Tid: " << gettid() << " ]"; std::string help_message =
"\nUsage: " + std::string(argv[0]) + " Options:\n" +
"\t-v, --version : Version of " + std::string(argv[0]) + ".\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";
static struct option long_options[] = {
{"help", no_argument, nullptr, 'h'},
{"version", no_argument, nullptr, 'v'},
{"rx_tcpport", required_argument, nullptr, 't'},
{"port", required_argument, nullptr, 'p'},
{"uid", required_argument, nullptr, 'u'},
{nullptr, 0, nullptr, 0}};
int option_index = 0;
int opt = 0;
while (-1 != (opt = getopt_long(argc, argv, "hvt:p:u:", long_options,
&option_index))) {
switch (opt) {
case 't':
LOG(sls::logWARNING)
<< "Deprecated option. Please use 'p' or '--port'.";
//[[fallthrough]]; TODO: for when we update to c++17
case 'p':
try {
port = sls::StringTo<uint16_t>(optarg);
} catch (...) {
throw sls::RuntimeError("Could not scan port number." +
help_message);
}
break;
case 'u':
try {
userid = sls::StringTo<uint32_t>(optarg);
} catch (...) {
throw sls::RuntimeError("Invalid uid." + help_message);
}
break;
case 'h':
std::cout << help_message << std::endl;
exit(EXIT_SUCCESS);
default:
throw sls::RuntimeError(help_message);
}
}
LOG(sls::logINFOBLUE) << "Current Process [ Tid: " << gettid() << " ]";
LOG(sls::logINFO) << "Port: " << port;
// set effective id if provided
if (userid != static_cast<uid_t>(-1)) {
if (geteuid() == userid) {
LOG(sls::logINFO)
<< "Process already has the same Effective UID " << userid;
} else {
if (seteuid(userid) != 0) {
std::ostringstream oss;
oss << "Could not set Effective UID to " << userid;
throw sls::RuntimeError(oss.str());
}
if (geteuid() != userid) {
std::ostringstream oss;
oss << "Could not set Effective UID to " << userid << ". Got "
<< geteuid();
throw sls::RuntimeError(oss.str());
}
LOG(sls::logINFO) << "Process Effective UID changed to " << userid;
}
}
// Catch signal SIGINT to close files and call destructors properly // Catch signal SIGINT to close files and call destructors properly
struct sigaction sa; struct sigaction sa;
@ -47,8 +124,9 @@ int main(int argc, char *argv[]) {
LOG(sls::logERROR) << "Could not set handler function for SIGPIPE"; LOG(sls::logERROR) << "Could not set handler function for SIGPIPE";
} }
sem_init(&semaphore, 1, 0);
try { try {
sls::Receiver r(argc, argv); sls::Receiver r(port);
LOG(sls::logINFO) << "[ Press \'Ctrl+c\' to exit ]"; LOG(sls::logINFO) << "[ Press \'Ctrl+c\' to exit ]";
sem_wait(&semaphore); sem_wait(&semaphore);
sem_destroy(&semaphore); sem_destroy(&semaphore);