00001
00002
00003
00004
00005
00006 #pragma GCC diagnostic push
00007 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
00008 #include <xmlrpc-c/base.hpp>
00009 #include <xmlrpc-c/registry.hpp>
00010 #include <xmlrpc-c/server_abyss.hpp>
00011 #include <xmlrpc-c/girerr.hpp>
00012 #pragma GCC diagnostic pop
00013 #include <stdexcept>
00014 #include <iostream>
00015 #include <limits>
00016 #include <memory>
00017 #include <cstdint>
00018
00019 #include <sys/socket.h>
00020 #include <netinet/in.h>
00021 #include <errno.h>
00022 #include <cstring>
00023
00024 #include "canvas/Persistency/Provenance/RunID.h"
00025
00026 #include "artdaq/ExternalComms/xmlrpc_commander.hh"
00027 #include "artdaq/DAQdata/Globals.hh"
00028 #include "fhiclcpp/make_ParameterSet.h"
00029
00030 namespace artdaq
00031 {
00038 std::string exception_msg(const std::runtime_error& er,
00039 const std::string& helpText = "execute request")
00040 {
00041 std::string msg("Exception when trying to ");
00042 msg.append(helpText);
00043 msg.append(": ");
00044 msg.append(er.what());
00045 if (msg[msg.size() - 1] == '\n') msg.erase(msg.size() - 1);
00046 return msg;
00047 }
00048
00055 std::string exception_msg(const art::Exception& er,
00056 const std::string& helpText)
00057 {
00058 std::string msg("Exception when trying to ");
00059 msg.append(helpText);
00060 msg.append(": ");
00061 msg.append(er.what());
00062 if (msg[msg.size() - 1] == '\n') msg.erase(msg.size() - 1);
00063 return msg;
00064 }
00065
00072 std::string exception_msg(const cet::exception& er,
00073 const std::string& helpText)
00074 {
00075 std::string msg("Exception when trying to ");
00076 msg.append(helpText);
00077 msg.append(": ");
00078 msg.append(er.what());
00079 if (msg[msg.size() - 1] == '\n') msg.erase(msg.size() - 1);
00080 return msg;
00081 }
00082
00089 std::string exception_msg(const std::string& erText,
00090 const std::string& helpText)
00091 {
00092 std::string msg("Exception when trying to ");
00093 msg.append(helpText);
00094 msg.append(": ");
00095 msg.append(erText);
00096 if (msg[msg.size() - 1] == '\n') msg.erase(msg.size() - 1);
00097 return msg;
00098 }
00099
00100
00118 class cmd_ : public xmlrpc_c::method
00119 {
00120 public:
00121
00122
00129 cmd_(xmlrpc_commander& c, const std::string& signature, const std::string& description) : _c(c)
00130 {
00131 _signature = signature;
00132 _help = description;
00133 }
00134
00140 void execute(const xmlrpc_c::paramList& paramList, xmlrpc_c::value* const retvalP) final;
00141
00142 protected:
00143
00144 xmlrpc_commander& _c;
00145
00151 virtual bool execute_(const xmlrpc_c::paramList&, xmlrpc_c::value* const retvalP) = 0;
00152
00162 template <typename T>
00163 T getParam(const xmlrpc_c::paramList& paramList, int index);
00164
00188 template <typename T>
00189 T getParam(const xmlrpc_c::paramList& paramList, int index, T default_value);
00190 };
00191
00192
00193
00194
00195 template <typename T>
00196 T cmd_::getParam(const xmlrpc_c::paramList&, int)
00197 {
00198 throw cet::exception("cmd_") << "Error in cmd_::getParam(): value type not supported" << std::endl;
00199 }
00200
00209 template <>
00210 uint64_t cmd_::getParam<uint64_t>(const xmlrpc_c::paramList& paramList, int index)
00211 {
00212 return boost::lexical_cast<uint64_t>(paramList.getInt(index));
00213 }
00214
00223 template <>
00224 std::string cmd_::getParam<std::string>(const xmlrpc_c::paramList& paramList, int index)
00225 {
00226 return static_cast<std::string>(paramList.getString(index));
00227 }
00228
00237 template <>
00238 art::RunID cmd_::getParam<art::RunID>(const xmlrpc_c::paramList& paramList, int index)
00239 {
00240 std::string run_number_string = paramList.getString(index);
00241 art::RunNumber_t run_number =
00242 boost::lexical_cast<art::RunNumber_t>(run_number_string);
00243 art::RunID run_id(run_number);
00244
00245 return run_id;
00246 }
00247
00256 template <>
00257 fhicl::ParameterSet cmd_::getParam<fhicl::ParameterSet>(const xmlrpc_c::paramList& paramList, int index)
00258 {
00259 std::string configString = paramList.getString(index);
00260 fhicl::ParameterSet pset;
00261 fhicl::make_ParameterSet(configString, pset);
00262
00263 return pset;
00264 }
00265
00266 template <typename T>
00267 T cmd_::getParam(const xmlrpc_c::paramList& paramList, int index,
00268 T default_value)
00269 {
00270 T val = default_value;
00271
00272 try
00273 {
00274 val = getParam<T>(paramList, index);
00275 }
00276 catch (const cet::exception& exception)
00277 {
00278 throw exception;
00279 }
00280 catch (...) {}
00281
00282 return val;
00283 }
00284
00285 void cmd_::execute(const xmlrpc_c::paramList& paramList, xmlrpc_c::value* const retvalP)
00286 {
00287 std::unique_lock<std::mutex> lk(_c.mutex_, std::try_to_lock);
00288 if (lk.owns_lock())
00289 {
00290 try
00291 {
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302 if (execute_(paramList, retvalP))
00303 {
00304 if (!retvalP->isInstantiated())
00305 {
00306 *retvalP = xmlrpc_c::value_string("Success");
00307 }
00308 }
00309 else
00310 {
00311 std::string problemReport = _c._commandable.report("transition_status");
00312 *retvalP = xmlrpc_c::value_string(problemReport);
00313 }
00314 }
00315 catch (std::runtime_error& er)
00316 {
00317 std::string msg = exception_msg(er, _help);
00318 *retvalP = xmlrpc_c::value_string(msg);
00319 TLOG_ERROR("XMLRPC_Commander") << msg << TLOG_ENDL;
00320 }
00321 catch (art::Exception& er)
00322 {
00323 std::string msg = exception_msg(er, _help);
00324 *retvalP = xmlrpc_c::value_string(msg);
00325 TLOG_ERROR("XMLRPC_Commander") << msg << TLOG_ENDL;
00326 }
00327 catch (cet::exception& er)
00328 {
00329 std::string msg = exception_msg(er, _help);
00330 *retvalP = xmlrpc_c::value_string(msg);
00331 TLOG_ERROR("XMLRPC_Commander") << msg << TLOG_ENDL;
00332 }
00333 catch (...)
00334 {
00335 std::string msg = exception_msg("Unknown exception", _help);
00336 *retvalP = xmlrpc_c::value_string(msg);
00337 TLOG_ERROR("XMLRPC_Commander") << msg << TLOG_ENDL;
00338 }
00339 }
00340 else
00341 {
00342 *retvalP = xmlrpc_c::value_string("busy");
00343 }
00344 }
00345
00346
00348
00349
00350
00351
00352
00353
00354
00355 #define GENERATE_INIT_TRANSITION(NAME, CALL, DESCRIPTION) \
00356 \
00357 class NAME ## _: public cmd_ { \
00358 \
00359 public: \
00360 \
00363 explicit NAME ## _(xmlrpc_commander& c): \
00364 cmd_(c, "s:sii", DESCRIPTION) {} \
00365 \
00366 \
00367 static const uint64_t defaultTimeout = 45; \
00368 \
00369 static const uint64_t defaultTimestamp = std::numeric_limits<const uint64_t>::max(); \
00370 \
00371 private: \
00372 bool execute_(const xmlrpc_c::paramList& paramList, xmlrpc_c::value* const retvalP ) { \
00373 \
00374 try { \
00375 getParam<fhicl::ParameterSet>(paramList, 0); \
00376 } catch (...) { \
00377 *retvalP = xmlrpc_c::value_string ("The "#NAME" message requires a single argument that is a string containing the initialization ParameterSet"); \
00378 return true; \
00379 } \
00380 \
00381 return _c._commandable.CALL( getParam<fhicl::ParameterSet>(paramList, 0), \
00382 getParam<uint64_t>(paramList, 1, defaultTimeout), \
00383 getParam<uint64_t>(paramList, 2, defaultTimestamp) \
00384 ); \
00385 } \
00386 };
00387
00388 GENERATE_INIT_TRANSITION(init, initialize, "initialize the program")
00389
00390 GENERATE_INIT_TRANSITION(soft_init, soft_initialize, "initialize software components in the program")
00391
00392 GENERATE_INIT_TRANSITION(reinit, reinitialize, "re-initialize the program")
00393
00394 #undef GENERATE_INIT_TRANSITION
00395
00397
00401 class start_ : public cmd_
00402 {
00403 public:
00408 explicit start_(xmlrpc_commander& c) :
00409 cmd_(c, "s:iii", "start the run") {}
00410
00412 static const uint64_t defaultTimeout = 45;
00414 static const uint64_t defaultTimestamp = std::numeric_limits<const uint64_t>::max();
00415
00416 private:
00417
00418 bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP) override
00419 {
00420 try
00421 {
00422 getParam<art::RunID>(paramList, 0);
00423 }
00424 catch (...)
00425 {
00426 *retvalP = xmlrpc_c::value_string("The start message requires the run number as an argument.");
00427 return true;
00428 }
00429
00430 return _c._commandable.start(getParam<art::RunID>(paramList, 0),
00431 getParam<uint64_t>(paramList, 1, defaultTimeout),
00432 getParam<uint64_t>(paramList, 2, defaultTimestamp)
00433 );
00434 }
00435 };
00436
00437
00439
00440
00441
00442
00443
00444
00445
00446 #define GENERATE_TIMEOUT_TIMESTAMP_TRANSITION(NAME, CALL, DESCRIPTION, TIMEOUT) \
00447 \
00448 class NAME ## _: public cmd_ { \
00449 \
00450 public: \
00451 \
00453 NAME ## _(xmlrpc_commander& c): \
00454 cmd_(c, "s:ii", DESCRIPTION) {} \
00455 \
00456 \
00457 static const uint64_t defaultTimeout = TIMEOUT ; \
00458 \
00459 static const uint64_t defaultTimestamp = std::numeric_limits<const uint64_t>::max(); \
00460 \
00461 private: \
00462 \
00463 bool execute_ (const xmlrpc_c::paramList& paramList , xmlrpc_c::value* const ) { \
00464 \
00465 return _c._commandable.CALL( getParam<uint64_t>(paramList, 0, defaultTimeout), \
00466 getParam<uint64_t>(paramList, 1, defaultTimestamp) \
00467 ); \
00468 } \
00469 };
00470
00471 GENERATE_TIMEOUT_TIMESTAMP_TRANSITION(pause, pause, "pause the program", 45)
00472
00473 GENERATE_TIMEOUT_TIMESTAMP_TRANSITION(resume, resume, "resume the program", 45)
00474
00475 GENERATE_TIMEOUT_TIMESTAMP_TRANSITION(stop, stop, "stop the program", 45)
00476
00477 #undef GENERATE_TIMEOUT_TIMESTAMP_TRANSITION
00478
00479
00483 class shutdown_ : public cmd_
00484 {
00485 public:
00490 shutdown_(xmlrpc_commander& c) :
00491 cmd_(c, "s:i", "shutdown the program") {}
00492
00494 static const uint64_t defaultTimeout = 45;
00495
00496 private:
00497
00498 bool execute_(const xmlrpc_c::paramList& paramList, xmlrpc_c::value* const)
00499 {
00500 return _c._commandable.shutdown(getParam<uint64_t>(paramList, 0, defaultTimeout));
00501 }
00502 };
00503
00504
00508 class status_ : public cmd_
00509 {
00510 public:
00515 status_(xmlrpc_commander& c) :
00516 cmd_(c, "s:n", "report the current state") {}
00517
00518 private:
00519
00520 bool execute_(xmlrpc_c::paramList const&, xmlrpc_c::value* const retvalP)
00521 {
00522 *retvalP = xmlrpc_c::value_string(_c._commandable.status());
00523 return true;
00524 }
00525 };
00526
00527
00531 class report_ : public cmd_
00532 {
00533 public:
00538 report_(xmlrpc_commander& c) :
00539 cmd_(c, "s:s", "report statistics") {}
00540
00541 private:
00542 bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP)
00543 {
00544 try
00545 {
00546 getParam<std::string>(paramList, 0);
00547 }
00548 catch (...)
00549 {
00550 *retvalP = xmlrpc_c::value_string("The report message requires a single argument that selects the type of statistics to be reported.");
00551 return true;
00552 }
00553
00554 *retvalP = xmlrpc_c::value_string(_c._commandable.report(getParam<std::string>(paramList, 0)));
00555 return true;
00556 }
00557 };
00558
00559
00563 class reset_stats_ : public cmd_
00564 {
00565 public:
00570 reset_stats_(xmlrpc_commander& c) :
00571 cmd_(c, "s:s", "reset statistics") {}
00572
00573 private:
00574 bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP)
00575 {
00576 try
00577 {
00578 getParam<std::string>(paramList, 0);
00579 }
00580 catch (...)
00581 {
00582 *retvalP = xmlrpc_c::value_string("The reset_stats message requires a single argument that selects the type of statistics to be reported.");
00583 return true;
00584 }
00585
00586 return _c._commandable.reset_stats(getParam<std::string>(paramList, 0));
00587 }
00588 };
00589
00593 class legal_commands_ : public cmd_
00594 {
00595 public:
00600 legal_commands_(xmlrpc_commander& c) :
00601 cmd_(c, "s:n", "return the currently legal commands") {}
00602
00603 private:
00604 bool execute_(xmlrpc_c::paramList const&, xmlrpc_c::value* const retvalP)
00605 {
00606 std::vector<std::string> cmdList = _c._commandable.legal_commands();
00607 std::string resultString;
00608
00609 for (auto& cmd : cmdList)
00610 {
00611 resultString.append(cmd + " ");
00612 if (cmd == "shutdown")
00613 {
00614 resultString.append(" reset");
00615 }
00616 }
00617 *retvalP = xmlrpc_c::value_string(resultString);
00618
00619 return true;
00620 }
00621 };
00622
00626 class register_monitor_ : public cmd_
00627 {
00628 public:
00633 register_monitor_(xmlrpc_commander& c) :
00634 cmd_(c, "s:s", "Get notified of a new monitor") {}
00635
00636 private:
00637 bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP)
00638 {
00639 try
00640 {
00641 getParam<fhicl::ParameterSet>(paramList, 0);
00642 }
00643 catch (...)
00644 {
00645 *retvalP = xmlrpc_c::value_string("The register_monitor command expects a string representing the FHiCL definition of a Transfer plugin");
00646 return true;
00647 }
00648
00649 *retvalP = xmlrpc_c::value_string(_c._commandable.register_monitor(getParam<fhicl::ParameterSet>(paramList, 0)));
00650 return true;
00651 }
00652 };
00653
00657 class unregister_monitor_ : public cmd_
00658 {
00659 public:
00664 unregister_monitor_(xmlrpc_commander& c) :
00665 cmd_(c, "s:s", "Remove a monitor") {}
00666
00667 private:
00668 bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP)
00669 {
00670 try
00671 {
00672 getParam<std::string>(paramList, 0);
00673 }
00674 catch (...)
00675 {
00676 *retvalP = xmlrpc_c::value_string("The unregister_monitor command expects a string representing the label of the monitor to be removed");
00677 return true;
00678 }
00679
00680 *retvalP = xmlrpc_c::value_string(_c._commandable.unregister_monitor(getParam<std::string>(paramList, 0)));
00681 return true;
00682 }
00683 };
00684
00685
00686
00687
00688
00689
00690
00691 #if 0
00692 class shutdown_ : public xmlrpc_c::registry::shutdown {
00693 public:
00694 shutdown_(xmlrpc_c::serverAbyss *server) : _server(server) {}
00695
00696 virtual void doit(const std::string& paramString, void*) const {
00697 TLOG_INFO("XMLRPC_Commander") << "A shutdown command was sent "
00698 << "with parameter "
00699 << paramString << "\"" << TLOG_ENDL;
00700 _server->terminate();
00701 }
00702 private:
00703 xmlrpc_c::serverAbyss *_server;
00704 };
00705 #endif
00706
00707
00708
00709 xmlrpc_commander::xmlrpc_commander(int port, artdaq::Commandable& commandable) :
00710 _port(port)
00711 , _commandable(commandable) {}
00712
00713 void xmlrpc_commander::run() try
00714 {
00715 xmlrpc_c::registry registry;
00716
00717 #define register_method(m) \
00718 xmlrpc_c::methodPtr const ptr_ ## m(new m ## _(*this));\
00719 registry.addMethod ("daq." #m, ptr_ ## m);
00720
00721 register_method(init);
00722 register_method(soft_init);
00723 register_method(reinit);
00724 register_method(start);
00725 register_method(status);
00726 register_method(report);
00727 register_method(stop);
00728 register_method(pause);
00729 register_method(resume);
00730 register_method(reset_stats);
00731 register_method(register_monitor);
00732 register_method(unregister_monitor);
00733 register_method(legal_commands);
00734
00735 register_method(shutdown);
00736
00737
00738 xmlrpc_c::methodPtr const ptr_reset(new shutdown_(*this));
00739 registry.addMethod("daq.reset", ptr_reset);
00740
00741 #undef register_method
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758 XMLRPC_SOCKET socket_file_descriptor = socket(PF_INET, SOCK_STREAM, 0);
00759
00760 if (socket_file_descriptor < 0)
00761 {
00762 throw cet::exception("xmlrpc_commander::run") <<
00763 "Problem with the socket() call; C-style errno == " <<
00764 errno << " (" << strerror(errno) << ")";
00765 }
00766
00767 int enable = 1;
00768 int retval = setsockopt(socket_file_descriptor,
00769 SOL_SOCKET, SO_REUSEADDR,
00770 &enable, sizeof(int));
00771
00772 if (retval < 0)
00773 {
00774 throw cet::exception("xmlrpc_commander::run") <<
00775 "Problem with the call to setsockopt(); C-style errno == " <<
00776 errno << " (" << strerror(errno) << ")";
00777 }
00778
00779 struct sockaddr_in sockAddr;
00780
00781 sockAddr.sin_family = AF_INET;
00782 sockAddr.sin_port = htons(_port);
00783 sockAddr.sin_addr.s_addr = 0;
00784
00785 retval = bind(socket_file_descriptor,
00786 reinterpret_cast<struct sockaddr*>(&sockAddr),
00787 sizeof(sockAddr));
00788
00789 if (retval != 0)
00790 {
00791 close(socket_file_descriptor);
00792 throw cet::exception("xmlrpc_commander::run") <<
00793 "Problem with the bind() call; C-style errno == " <<
00794 errno << " (" << strerror(errno) << ")";
00795 }
00796
00797 xmlrpc_c::serverAbyss server(xmlrpc_c::serverAbyss::constrOpt().registryP(®istry).socketFd(socket_file_descriptor));
00798
00799 #if 0
00800 shutdown_ shutdown_obj(&server);
00801 registry.setShutdown(&shutdown_obj);
00802 #endif
00803
00804 TLOG_DEBUG("XMLRPC_Commander") << "running server" << TLOG_ENDL;
00805
00806
00807
00808
00809
00810
00811
00812 try
00813 {
00814 server.run();
00815 }
00816 catch (...)
00817 {
00818 TLOG_WARNING("XMLRPC_Commander") << "server threw an exception; closing the socket and rethrowing" << TLOG_ENDL;
00819 close(socket_file_descriptor);
00820 throw;
00821 }
00822
00823 close(socket_file_descriptor);
00824 TLOG_DEBUG("XMLRPC_Commander") << "server terminated" << TLOG_ENDL;
00825 }
00826 catch (...)
00827 {
00828 throw;
00829 }
00830 }