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:
/**
* Constructor
* Starts up a Receiver server. Reads configuration file, options, and
* assembles a Receiver using TCP and UDP detector interfaces
* Starts up a Receiver server.
* Assembles a Receiver using TCP and UDP detector interfaces
* throws an exception in case of failure
* @param argc from command line
* @param argv from command line
* @param port TCP/IP port number
*/
Receiver(int argc, char *argv[]);
/**
* 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(uint16_t port = 1954);
~Receiver();

View File

@ -7,10 +7,12 @@
#include "sls/container_utils.h"
#include "sls/logger.h"
#include "sls/sls_detector_defs.h"
#include "sls/sls_detector_exceptions.h"
#include "sls/versionAPI.h"
#include <csignal> //SIGINT
#include <cstring>
#include <getopt.h>
#include <iostream>
#include <semaphore.h>
#include <sys/wait.h> //wait
@ -29,28 +31,6 @@
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
* enabled) if registerCallBackRawDataReady or
@ -153,6 +133,60 @@ void GetData(slsDetectorDefs::sls_receiver_header &header,
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
*
@ -161,60 +195,109 @@ void GetData(slsDetectorDefs::sls_receiver_header &header,
* - Default Start TCP port is 1954
*/
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
if (argc == 2) {
std::string sargv1 = std::string(argv[1]);
if (sargv1 == "--version" || sargv1 == "-v") {
std::cout << "slsMultiReceiver Version: " << APIRECEIVER
<< std::endl;
std::string help_message = getHelpMessage();
static struct option long_options[] = {
{"help", no_argument, nullptr, 'h'},
{"version", no_argument, nullptr, 'v'},
{"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);
default:
// maintain backward compatibility of [startport] [num-receivers]
// [callback]
GetDeprecatedCommandLineOptions(argc, argv, startPort, numReceivers,
callbackEnabled);
throw sls::RuntimeError(help_message);
}
}
/** - set default values */
int numReceivers = 1;
uint16_t startTCPPort = DEFAULT_TCP_RX_PORTNO;
int withCallback = 0;
sem_init(&semaphore, 1, 0);
LOG(sls::logINFOBLUE) << "Current Process [ Tid: " << gettid() << ']';
LOG(sls::logINFO) << "Number of Receivers: " << numReceivers;
LOG(sls::logINFO) << "Start TCP Port: " << startPort;
LOG(sls::logINFO) << "Callback Enable: " << callbackEnabled;
/** - get number of receivers and start tcp port from command line
* arguments */
if (argc > 1) {
try {
if (argc == 3 || argc == 4) {
startTCPPort = sls::StringTo<uint16_t>(argv[1]);
if (startTCPPort == 0) {
throw std::runtime_error("Invalid start tcp port");
}
numReceivers = std::stoi(argv[2]);
if (numReceivers > 1024) {
cprintf(RED,
"Did you mix up the order of the arguments?\n%s\n",
getHelpMessage().c_str());
return EXIT_FAILURE;
}
if (numReceivers == 0) {
cprintf(RED, "Invalid number of receivers.\n%s\n",
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;
// 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;
}
}
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 */
struct sigaction sa;
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
// of handler
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,
@ -234,10 +317,11 @@ int main(int argc, char *argv[]) {
sigemptyset(&asa.sa_mask); // dont block additional signals during
// invocation of handler
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 */
sem_init(&semaphore, 1, 0);
for (int i = 0; i < numReceivers; ++i) {
/** - 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
* processes */
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);
}
/** - if child process */
else if (pid == 0) {
cprintf(BLUE, "Child process %d [ Tid: %ld ]\n", i, (long)gettid());
LOG(sls::logINFOBLUE)
<< "Child process " << i << " [ Tid: " << gettid() << ']';
try {
uint16_t port = startTCPPort + i;
uint16_t port = startPort + i;
sls::Receiver receiver(port);
/** - register callbacks. remember to set file write enable
* to 0 (using the client) if we should not write files and you
* will write data using the callbacks */
if (withCallback) {
if (callbackEnabled) {
/** - Call back for start acquisition */
cprintf(BLUE, "Registering StartAcq()\n");
LOG(sls::logINFOBLUE) << "Registering StartAcq()";
receiver.registerCallBackStartAcquisition(StartAcq,
nullptr);
/** - Call back for acquisition finished */
cprintf(BLUE, "Registering AcquisitionFinished()\n");
LOG(sls::logINFOBLUE)
<< "Registering AcquisitionFinished()";
receiver.registerCallBackAcquisitionFinished(
AcquisitionFinished, nullptr);
/* - Call back for raw data */
cprintf(BLUE, "Registering GetData() \n");
LOG(sls::logINFOBLUE) << "Registering GetData()";
receiver.registerCallBackRawDataReady(GetData, nullptr);
}
@ -288,8 +375,8 @@ int main(int argc, char *argv[]) {
throw;
}
cprintf(BLUE, "Exiting Child Process [ Tid: %ld ]\n",
(long)gettid());
LOG(sls::logINFOBLUE)
<< "Exiting Child Process [ Tid: " << gettid() << ']';
exit(EXIT_SUCCESS);
}
}
@ -301,12 +388,12 @@ int main(int argc, char *argv[]) {
sigemptyset(&sa.sa_mask); // dont block additional signals during invocation
// of handler
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 */
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 */
for (;;) {
@ -315,18 +402,19 @@ int main(int argc, char *argv[]) {
// no child closed
if (childPid == -1) {
if (errno == ECHILD) {
cprintf(GREEN, "All Child Processes have been closed\n");
LOG(sls::logINFOGREEN)
<< "All Child Processes have been closed";
break;
} else {
cprintf(RED, "Unexpected error from waitpid(): (%s)\n",
strerror(errno));
LOG(sls::logERROR)
<< "Unexpected error from waitpid(): " << strerror(errno);
break;
}
}
// child closed
cprintf(BLUE, "Exiting Child Process [ Tid: %ld ]\n",
(long int)childPid);
LOG(sls::logINFOBLUE)
<< "Exiting Child Process [ Tid: " << childPid << ']';
}
std::cout << "Goodbye!\n";

View File

@ -27,106 +27,9 @@ namespace sls {
Receiver::~Receiver() = default;
Receiver::Receiver(int argc, char *argv[]) : tcpipInterface(nullptr) {
// options
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);
Receiver::Receiver(uint16_t port) {
validatePortNumber(port);
tcpipInterface = make_unique<ClientInterface>(port);
}
std::string Receiver::getReceiverVersion() {

View File

@ -2,11 +2,13 @@
// Copyright (C) 2021 Contributors to the SLS Detector Package
/* slsReceiver */
#include "sls/Receiver.h"
#include "sls/ToString.h"
#include "sls/container_utils.h"
#include "sls/logger.h"
#include "sls/sls_detector_defs.h"
#include <csignal> //SIGINT
#include <getopt.h>
#include <semaphore.h>
#include <unistd.h>
@ -22,9 +24,84 @@ void sigInterruptHandler(int p) { sem_post(&semaphore); }
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
struct sigaction sa;
@ -47,8 +124,9 @@ int main(int argc, char *argv[]) {
LOG(sls::logERROR) << "Could not set handler function for SIGPIPE";
}
sem_init(&semaphore, 1, 0);
try {
sls::Receiver r(argc, argv);
sls::Receiver r(port);
LOG(sls::logINFO) << "[ Press \'Ctrl+c\' to exit ]";
sem_wait(&semaphore);
sem_destroy(&semaphore);