artdaq  v2_03_02
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Pages
xmlrpc_commander.cc
1 /* DarkSide 50 DAQ program
2  * This file add the xmlrpc commander as a client to the SC
3  * Author: Alessandro Razeto <Alessandro.Razeto@ge.infn.it>
4  */
5 
6 #pragma GCC diagnostic push
7 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
8 #include <xmlrpc-c/base.hpp>
9 #include <xmlrpc-c/registry.hpp>
10 #include <xmlrpc-c/server_abyss.hpp>
11 #include <xmlrpc-c/girerr.hpp>
12 #pragma GCC diagnostic pop
13 #include <stdexcept>
14 #include <iostream>
15 #include <limits>
16 #include <memory>
17 #include <cstdint>
18 
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <errno.h>
22 #include <cstring>
23 
24 #include "canvas/Persistency/Provenance/RunID.h"
25 
26 #include "artdaq/ExternalComms/xmlrpc_commander.hh"
27 #include "artdaq/DAQdata/Globals.hh"
28 #include "fhiclcpp/make_ParameterSet.h"
29 
30 namespace artdaq
31 {
38  std::string exception_msg(const std::runtime_error& er,
39  const std::string& helpText = "execute request")
40  {
41  std::string msg("Exception when trying to ");
42  msg.append(helpText);
43  msg.append(": ");
44  msg.append(er.what()); //std::string(er.what ()).substr (2);
45  if (msg[msg.size() - 1] == '\n') msg.erase(msg.size() - 1);
46  return msg;
47  }
48 
55  std::string exception_msg(const art::Exception& er,
56  const std::string& helpText)
57  {
58  std::string msg("Exception when trying to ");
59  msg.append(helpText);
60  msg.append(": ");
61  msg.append(er.what());
62  if (msg[msg.size() - 1] == '\n') msg.erase(msg.size() - 1);
63  return msg;
64  }
65 
72  std::string exception_msg(const cet::exception& er,
73  const std::string& helpText)
74  {
75  std::string msg("Exception when trying to ");
76  msg.append(helpText);
77  msg.append(": ");
78  msg.append(er.what());
79  if (msg[msg.size() - 1] == '\n') msg.erase(msg.size() - 1);
80  return msg;
81  }
82 
89  std::string exception_msg(const std::string& erText,
90  const std::string& helpText)
91  {
92  std::string msg("Exception when trying to ");
93  msg.append(helpText);
94  msg.append(": ");
95  msg.append(erText);
96  if (msg[msg.size() - 1] == '\n') msg.erase(msg.size() - 1);
97  return msg;
98  }
99 
100 
118  class cmd_ : public xmlrpc_c::method
119  {
120  public:
121 
122  // Can't seem to initialize "_signature" and "_help" in the initialization list...
129  cmd_(xmlrpc_commander& c, const std::string& signature, const std::string& description) : _c(c)
130  {
131  _signature = signature;
132  _help = description;
133  }
134 
140  void execute(const xmlrpc_c::paramList& paramList, xmlrpc_c::value* const retvalP) final;
141 
142  protected:
143 
145 
151  virtual bool execute_(const xmlrpc_c::paramList&, xmlrpc_c::value* const retvalP) = 0;
152 
162  template <typename T>
163  T getParam(const xmlrpc_c::paramList& paramList, int index);
164 
188  template <typename T>
189  T getParam(const xmlrpc_c::paramList& paramList, int index, T default_value);
190  };
191 
192  // Users are only allowed to call getParam for predefined types; see
193  // template specializations below this default function
194 
195  template <typename T>
196  T cmd_::getParam(const xmlrpc_c::paramList&, int)
197  {
198  throw cet::exception("cmd_") << "Error in cmd_::getParam(): value type not supported" << std::endl;
199  }
200 
209  template <>
210  uint64_t cmd_::getParam<uint64_t>(const xmlrpc_c::paramList& paramList, int index)
211  {
212  return boost::lexical_cast<uint64_t>(paramList.getInt(index));
213  }
214 
223  template <>
224  std::string cmd_::getParam<std::string>(const xmlrpc_c::paramList& paramList, int index)
225  {
226  return static_cast<std::string>(paramList.getString(index));
227  }
228 
237  template <>
238  art::RunID cmd_::getParam<art::RunID>(const xmlrpc_c::paramList& paramList, int index)
239  {
240  std::string run_number_string = paramList.getString(index);
241  art::RunNumber_t run_number =
242  boost::lexical_cast<art::RunNumber_t>(run_number_string);
243  art::RunID run_id(run_number);
244 
245  return run_id;
246  }
247 
256  template <>
257  fhicl::ParameterSet cmd_::getParam<fhicl::ParameterSet>(const xmlrpc_c::paramList& paramList, int index)
258  {
259  std::string configString = paramList.getString(index);
260  fhicl::ParameterSet pset;
261  fhicl::make_ParameterSet(configString, pset);
262 
263  return pset;
264  }
265 
266  template <typename T>
267  T cmd_::getParam(const xmlrpc_c::paramList& paramList, int index,
268  T default_value)
269  {
270  T val = default_value;
271 
272  try
273  {
274  val = getParam<T>(paramList, index);
275  }
276  catch (const cet::exception& exception)
277  {
278  throw exception;
279  }
280  catch (...) {}
281 
282  return val;
283  }
284 
285  void cmd_::execute(const xmlrpc_c::paramList& paramList, xmlrpc_c::value* const retvalP)
286  {
287  std::unique_lock<std::mutex> lk(_c.mutex_, std::try_to_lock);
288  if (lk.owns_lock())
289  {
290  try
291  {
292  // JCF, 9/4/14
293 
294  // Assuming the execute_ function returns true, then if the
295  // retvalP argument was untouched, assign it the string
296  // "Success"
297 
298  // See
299  // http://xmlrpc-c.sourceforge.net/doc/libxmlrpc++.html#isinstantiated
300  // for more on the concept of instantiation in xmlrpc_c::value objects
301 
302  if (execute_(paramList, retvalP))
303  {
304  if (!retvalP->isInstantiated())
305  {
306  *retvalP = xmlrpc_c::value_string("Success");
307  }
308  }
309  else
310  {
311  std::string problemReport = _c._commandable.report("transition_status");
312  *retvalP = xmlrpc_c::value_string(problemReport);
313  }
314  }
315  catch (std::runtime_error& er)
316  {
317  std::string msg = exception_msg(er, _help);
318  *retvalP = xmlrpc_c::value_string(msg);
319  TLOG_ERROR("XMLRPC_Commander") << msg << TLOG_ENDL;
320  }
321  catch (art::Exception& er)
322  {
323  std::string msg = exception_msg(er, _help);
324  *retvalP = xmlrpc_c::value_string(msg);
325  TLOG_ERROR("XMLRPC_Commander") << msg << TLOG_ENDL;
326  }
327  catch (cet::exception& er)
328  {
329  std::string msg = exception_msg(er, _help);
330  *retvalP = xmlrpc_c::value_string(msg);
331  TLOG_ERROR("XMLRPC_Commander") << msg << TLOG_ENDL;
332  }
333  catch (...)
334  {
335  std::string msg = exception_msg("Unknown exception", _help);
336  *retvalP = xmlrpc_c::value_string(msg);
337  TLOG_ERROR("XMLRPC_Commander") << msg << TLOG_ENDL;
338  }
339  }
340  else
341  {
342  *retvalP = xmlrpc_c::value_string("busy");
343  }
344  }
345 
346 
348 
349  // JCF, 9/5/14
350 
351  // The three "init" transitions all take a FHiCL parameter list, and
352  // optionally a timeout and a timestamp; thus we can kill three birds
353  // with one stone in the GENERATE_INIT_TRANSITION macro
354 
355 #define GENERATE_INIT_TRANSITION(NAME, CALL, DESCRIPTION) \
356  \
357  class NAME ## _: public cmd_ { \
358  \
359  public: \
360  \
363  explicit NAME ## _(xmlrpc_commander& c): \
364  cmd_(c, "s:sii", DESCRIPTION) {} \
365  \
366  \
367  static const uint64_t defaultTimeout = 45; \
368  \
369  static const uint64_t defaultTimestamp = std::numeric_limits<const uint64_t>::max(); \
370  \
371  private: \
372  bool execute_(const xmlrpc_c::paramList& paramList, xmlrpc_c::value* const retvalP ) { \
373  \
374  try { \
375  getParam<fhicl::ParameterSet>(paramList, 0); \
376  } catch (...) { \
377  *retvalP = xmlrpc_c::value_string ("The "#NAME" message requires a single argument that is a string containing the initialization ParameterSet"); \
378  return true; \
379  } \
380  \
381  return _c._commandable.CALL( getParam<fhicl::ParameterSet>(paramList, 0), \
382  getParam<uint64_t>(paramList, 1, defaultTimeout), \
383  getParam<uint64_t>(paramList, 2, defaultTimestamp) \
384  ); \
385  } \
386  };
387 
388  GENERATE_INIT_TRANSITION(init, initialize, "initialize the program")
389 
390  GENERATE_INIT_TRANSITION(soft_init, soft_initialize, "initialize software components in the program")
391 
392  GENERATE_INIT_TRANSITION(reinit, reinitialize, "re-initialize the program")
393 
394 #undef GENERATE_INIT_TRANSITION
395 
397 
401  class start_ : public cmd_
402  {
403  public:
408  explicit start_(xmlrpc_commander& c) :
409  cmd_(c, "s:iii", "start the run") {}
410 
412  static const uint64_t defaultTimeout = 45;
414  static const uint64_t defaultTimestamp = std::numeric_limits<const uint64_t>::max();
415 
416  private:
417 
418  bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP) override
419  {
420  try
421  {
422  getParam<art::RunID>(paramList, 0);
423  }
424  catch (...)
425  {
426  *retvalP = xmlrpc_c::value_string("The start message requires the run number as an argument.");
427  return true;
428  }
429 
430  return _c._commandable.start(getParam<art::RunID>(paramList, 0),
431  getParam<uint64_t>(paramList, 1, defaultTimeout),
432  getParam<uint64_t>(paramList, 2, defaultTimestamp)
433  );
434  }
435  };
436 
437 
439 
440  // JCF, 9/5/14
441 
442  // "pause", "resume" and "stop" all take an optional timeout and
443  // timestamp parameter, so we can generate them all with the
444  // GENERATE_TIMEOUT_TIMESTAMP_TRANSITION macro
445 
446 #define GENERATE_TIMEOUT_TIMESTAMP_TRANSITION(NAME, CALL, DESCRIPTION, TIMEOUT) \
447  \
448  class NAME ## _: public cmd_ { \
449  \
450 public: \
451  \
453  NAME ## _(xmlrpc_commander& c): \
454  cmd_(c, "s:ii", DESCRIPTION) {} \
455  \
456  \
457  static const uint64_t defaultTimeout = TIMEOUT ; \
458  \
459  static const uint64_t defaultTimestamp = std::numeric_limits<const uint64_t>::max(); \
460  \
461 private: \
462  \
463  bool execute_ (const xmlrpc_c::paramList& paramList , xmlrpc_c::value* const ) { \
464  \
465  return _c._commandable.CALL( getParam<uint64_t>(paramList, 0, defaultTimeout), \
466  getParam<uint64_t>(paramList, 1, defaultTimestamp) \
467  ); \
468  } \
469  };
470 
471  GENERATE_TIMEOUT_TIMESTAMP_TRANSITION(pause, pause, "pause the program", 45)
472 
473  GENERATE_TIMEOUT_TIMESTAMP_TRANSITION(resume, resume, "resume the program", 45)
474 
475  GENERATE_TIMEOUT_TIMESTAMP_TRANSITION(stop, stop, "stop the program", 45)
476 
477 #undef GENERATE_TIMEOUT_TIMESTAMP_TRANSITION
478 
479 
483  class shutdown_ : public cmd_
484  {
485  public:
491  cmd_(c, "s:i", "shutdown the program") {}
492 
494  static const uint64_t defaultTimeout = 45;
495 
496  private:
497 
498  bool execute_(const xmlrpc_c::paramList& paramList, xmlrpc_c::value* const)
499  {
500  return _c._commandable.shutdown(getParam<uint64_t>(paramList, 0, defaultTimeout));
501  }
502  };
503 
504 
508  class status_ : public cmd_
509  {
510  public:
516  cmd_(c, "s:n", "report the current state") {}
517 
518  private:
519 
520  bool execute_(xmlrpc_c::paramList const&, xmlrpc_c::value* const retvalP)
521  {
522  *retvalP = xmlrpc_c::value_string(_c._commandable.status());
523  return true;
524  }
525  };
526 
527 
531  class report_ : public cmd_
532  {
533  public:
539  cmd_(c, "s:s", "report statistics") {}
540 
541  private:
542  bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP)
543  {
544  try
545  {
546  getParam<std::string>(paramList, 0);
547  }
548  catch (...)
549  {
550  *retvalP = xmlrpc_c::value_string("The report message requires a single argument that selects the type of statistics to be reported.");
551  return true;
552  }
553 
554  *retvalP = xmlrpc_c::value_string(_c._commandable.report(getParam<std::string>(paramList, 0)));
555  return true;
556  }
557  };
558 
559 
563  class reset_stats_ : public cmd_
564  {
565  public:
571  cmd_(c, "s:s", "reset statistics") {}
572 
573  private:
574  bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP)
575  {
576  try
577  {
578  getParam<std::string>(paramList, 0);
579  }
580  catch (...)
581  {
582  *retvalP = xmlrpc_c::value_string("The reset_stats message requires a single argument that selects the type of statistics to be reported.");
583  return true;
584  }
585 
586  return _c._commandable.reset_stats(getParam<std::string>(paramList, 0));
587  }
588  };
589 
593  class legal_commands_ : public cmd_
594  {
595  public:
601  cmd_(c, "s:n", "return the currently legal commands") {}
602 
603  private:
604  bool execute_(xmlrpc_c::paramList const&, xmlrpc_c::value* const retvalP)
605  {
606  std::vector<std::string> cmdList = _c._commandable.legal_commands();
607  std::string resultString;
608 
609  for (auto& cmd : cmdList)
610  {
611  resultString.append(cmd + " ");
612  if (cmd == "shutdown")
613  {
614  resultString.append(" reset");
615  }
616  }
617  *retvalP = xmlrpc_c::value_string(resultString);
618 
619  return true;
620  }
621  };
622 
626  class register_monitor_ : public cmd_
627  {
628  public:
634  cmd_(c, "s:s", "Get notified of a new monitor") {}
635 
636  private:
637  bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP)
638  {
639  try
640  {
641  getParam<fhicl::ParameterSet>(paramList, 0);
642  }
643  catch (...)
644  {
645  *retvalP = xmlrpc_c::value_string("The register_monitor command expects a string representing the FHiCL definition of a Transfer plugin");
646  return true;
647  }
648 
649  *retvalP = xmlrpc_c::value_string(_c._commandable.register_monitor(getParam<fhicl::ParameterSet>(paramList, 0)));
650  return true;
651  }
652  };
653 
657  class unregister_monitor_ : public cmd_
658  {
659  public:
665  cmd_(c, "s:s", "Remove a monitor") {}
666 
667  private:
668  bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP)
669  {
670  try
671  {
672  getParam<std::string>(paramList, 0);
673  }
674  catch (...)
675  {
676  *retvalP = xmlrpc_c::value_string("The unregister_monitor command expects a string representing the label of the monitor to be removed");
677  return true;
678  }
679 
680  *retvalP = xmlrpc_c::value_string(_c._commandable.unregister_monitor(getParam<std::string>(paramList, 0)));
681  return true;
682  }
683  };
684 
685 
686  // JCF, 9/4/14
687 
688  // Not sure if anyone was planning to resurrect this code by changing
689  // the preprocessor decision; as such, I'll leave it in for now...
690 
691 #if 0
692  class shutdown_ : public xmlrpc_c::registry::shutdown {
693  public:
694  shutdown_(xmlrpc_c::serverAbyss *server) : _server(server) {}
695 
696  virtual void doit(const std::string& paramString, void*) const {
697  TLOG_INFO("XMLRPC_Commander") << "A shutdown command was sent "
698  << "with parameter "
699  << paramString << "\"" << TLOG_ENDL;
700  _server->terminate();
701  }
702  private:
703  xmlrpc_c::serverAbyss *_server;
704  };
705 #endif
706 
707 
708 
710  _port(port)
711  , _commandable(commandable) {}
712 
714 {
715  xmlrpc_c::registry registry;
716 
717 #define register_method(m) \
718  xmlrpc_c::methodPtr const ptr_ ## m(new m ## _(*this));\
719  registry.addMethod ("daq." #m, ptr_ ## m);
720 
721  register_method(init);
722  register_method(soft_init);
723  register_method(reinit);
724  register_method(start);
725  register_method(status);
726  register_method(report);
727  register_method(stop);
728  register_method(pause);
729  register_method(resume);
730  register_method(reset_stats);
731  register_method(register_monitor);
732  register_method(unregister_monitor);
733  register_method(legal_commands);
734 
735  register_method(shutdown);
736 
737  // alias "daq.reset" to the internal shutdown transition
738  xmlrpc_c::methodPtr const ptr_reset(new shutdown_(*this));
739  registry.addMethod("daq.reset", ptr_reset);
740 
741 #undef register_method
742 
743  // JCF, 6/3/15
744 
745  // In the following code, I configure a socket to have the
746  // SO_REUSEADDR option so that once an artdaq process closes, the
747  // port it was communicating on becomes immediately available
748  // (desirable if, say, the DAQ program is terminated and then
749  // immediately restarted)
750 
751  // Much of the following code is cribbed from
752  // http://fossies.org/linux/freeswitch/libs/xmlrpc-c/src/cpp/test/server_abyss.cpp
753 
754  // Below, "0" is the default protocol (in this case, given the IPv4
755  // Protocol Family (PF_INET) and the SOCK_STREAM communication
756  // method)
757 
758  XMLRPC_SOCKET socket_file_descriptor = socket(PF_INET, SOCK_STREAM, 0);
759 
760  if (socket_file_descriptor < 0)
761  {
762  throw cet::exception("xmlrpc_commander::run") <<
763  "Problem with the socket() call; C-style errno == " <<
764  errno << " (" << strerror(errno) << ")";
765  }
766 
767  int enable = 1;
768  int retval = setsockopt(socket_file_descriptor,
769  SOL_SOCKET, SO_REUSEADDR,
770  &enable, sizeof(int));
771 
772  if (retval < 0)
773  {
774  throw cet::exception("xmlrpc_commander::run") <<
775  "Problem with the call to setsockopt(); C-style errno == " <<
776  errno << " (" << strerror(errno) << ")";
777  }
778 
779  struct sockaddr_in sockAddr;
780 
781  sockAddr.sin_family = AF_INET;
782  sockAddr.sin_port = htons(_port);
783  sockAddr.sin_addr.s_addr = 0;
784 
785  retval = bind(socket_file_descriptor,
786  reinterpret_cast<struct sockaddr*>(&sockAddr),
787  sizeof(sockAddr));
788 
789  if (retval != 0)
790  {
791  close(socket_file_descriptor);
792  throw cet::exception("xmlrpc_commander::run") <<
793  "Problem with the bind() call; C-style errno == " <<
794  errno << " (" << strerror(errno) << ")";
795  }
796 
797  xmlrpc_c::serverAbyss server(xmlrpc_c::serverAbyss::constrOpt().registryP(&registry).socketFd(socket_file_descriptor));
798 
799 #if 0
800  shutdown_ shutdown_obj(&server);
801  registry.setShutdown(&shutdown_obj);
802 #endif
803 
804  TLOG_DEBUG("XMLRPC_Commander") << "running server" << TLOG_ENDL;
805 
806  // JCF, 6/3/15
807 
808  // Use a catch block to clean up (i.e., close the socket). An
809  // opportunity for RAII, although all control paths are limited to
810  // this section of the file...
811 
812  try
813  {
814  server.run();
815  }
816  catch (...)
817  {
818  TLOG_WARNING("XMLRPC_Commander") << "server threw an exception; closing the socket and rethrowing" << TLOG_ENDL;
819  close(socket_file_descriptor);
820  throw;
821  }
822 
823  close(socket_file_descriptor);
824  TLOG_DEBUG("XMLRPC_Commander") << "server terminated" << TLOG_ENDL;
825 }
826 catch (...)
827 {
828  throw;
829 }
830 } // namespace artdaq
cmd_(xmlrpc_commander &c, const std::string &signature, const std::string &description)
cmd_ Constructor
static const uint64_t defaultTimeout
virtual std::string report(std::string const &) const
Default report implementation returns current report_string.
Definition: Commandable.hh:121
Commandable is the base class for all artdaq components which implement the artdaq state machine...
Definition: Commandable.hh:20
legal_commands_(xmlrpc_commander &c)
legal_commands_ Constructor
Command class representing a start transition.
status_ Command class
report_ Command class
virtual std::string unregister_monitor(std::string const &)
Perform the unregister_monitor action.
Definition: Commandable.hh:168
The xmlrpc_commander class serves as the XMLRPC server run in each artdaq application.
static const uint64_t defaultTimestamp
The &quot;cmd_&quot; class serves as the base class for all artdaq&#39;s XML-RPC commands.
shutdown_ Command class
std::string status() const
Returns the current state of the Commandable.
Definition: Commandable.cc:182
std::string exception_msg(const std::runtime_error &er, const std::string &helpText="execute request")
Write an exception message.
virtual std::string register_monitor(fhicl::ParameterSet const &)
Perform the register_monitor action.
Definition: Commandable.hh:157
register_monitor_ Command class
virtual bool reset_stats(std::string const &which)
Virutal function which resets statistics.
Definition: Commandable.hh:138
legal_commands_ Command class
xmlrpc_commander & _c
The xmlrpc_commander instance that the command will be sent to.
status_(xmlrpc_commander &c)
status_ Constructor
virtual bool execute_(const xmlrpc_c::paramList &, xmlrpc_c::value *const retvalP)=0
&quot;execute_&quot; is a wrapper function around the call to the commandable object&#39;s function ...
register_monitor_(xmlrpc_commander &c)
register_monitor_ Constructor
static const uint64_t defaultTimeout
unregister_monitor_(xmlrpc_commander &c)
unregister_monitor_ Constructor
void execute(const xmlrpc_c::paramList &paramList, xmlrpc_c::value *const retvalP) final
Execute trhe command with the given parameters.
shutdown_(xmlrpc_commander &c)
shutdown_ Constructor
reset_stats_(xmlrpc_commander &c)
reset_stats_ Constructor
artdaq::Commandable & _commandable
The artdaq::Commandable object that this xmlrpc_commander sends commands to.
std::vector< std::string > legal_commands() const
Get the legal transition commands from the current state.
Definition: Commandable.cc:194
bool start(art::RunID id, uint64_t timeout, uint64_t timestamp)
Processes the start transition.
Definition: Commandable.cc:30
reset_stats_ Command class
report_(xmlrpc_commander &c)
report_ Constructor
xmlrpc_commander(int port, artdaq::Commandable &commandable)
xmlrpc_commander Constructor
start_(xmlrpc_commander &c)
start_ Command (cmd_ derived class) Constructor
void run()
Run the XMLRPC server.
std::mutex mutex_
XMLRPC mutex.
unregister_monitor_ Command class
T getParam(const xmlrpc_c::paramList &paramList, int index)
Get a parameter from the parameter list.
bool shutdown(uint64_t timeout)
Processes the shutdown transition.
Definition: Commandable.cc:106