diff --git a/slsReceiverSoftware/include/sls/Receiver.h b/slsReceiverSoftware/include/sls/Receiver.h index eb7c4b214..0a3a83482 100644 --- a/slsReceiverSoftware/include/sls/Receiver.h +++ b/slsReceiverSoftware/include/sls/Receiver.h @@ -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(); diff --git a/slsReceiverSoftware/src/MultiReceiverApp.cpp b/slsReceiverSoftware/src/MultiReceiverApp.cpp index 791666398..0c09e828a 100644 --- a/slsReceiverSoftware/src/MultiReceiverApp.cpp +++ b/slsReceiverSoftware/src/MultiReceiverApp.cpp @@ -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 //SIGINT #include +#include #include #include #include //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(argv[1]); + numReceivers = sls::StringTo(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(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(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(optarg); + } catch (...) { + throw sls::RuntimeError("Could not scan port number." + + help_message); + } + break; + + case 'c': + try { + callbackEnabled = sls::StringTo(optarg); + } catch (...) { + throw sls::RuntimeError("Invalid callback enable." + + help_message); + } + break; + + case 'u': + try { + userid = sls::StringTo(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(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(-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"; diff --git a/slsReceiverSoftware/src/Receiver.cpp b/slsReceiverSoftware/src/Receiver.cpp index 41a729be8..d81ecb33a 100644 --- a/slsReceiverSoftware/src/Receiver.cpp +++ b/slsReceiverSoftware/src/Receiver.cpp @@ -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 don’t 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 : TCP Communication Port with " - "client. Non-zero and 16 bit.\n" + - "\t-u, --uid : 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(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(-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(tcpip_port_no); -} - -Receiver::Receiver(uint16_t tcpip_port_no) { - // might throw an exception - tcpipInterface = make_unique(tcpip_port_no); +Receiver::Receiver(uint16_t port) { + validatePortNumber(port); + tcpipInterface = make_unique(port); } std::string Receiver::getReceiverVersion() { diff --git a/slsReceiverSoftware/src/ReceiverApp.cpp b/slsReceiverSoftware/src/ReceiverApp.cpp index 767339869..c4b679ce7 100644 --- a/slsReceiverSoftware/src/ReceiverApp.cpp +++ b/slsReceiverSoftware/src/ReceiverApp.cpp @@ -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 //SIGINT +#include #include #include @@ -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(optarg); + } catch (...) { + throw sls::RuntimeError("Could not scan port number." + + help_message); + } + break; + + case 'u': + try { + userid = sls::StringTo(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(-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);