artdaq  v3_07_01
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 #define _LIBCPP_ENABLE_CXX17_REMOVED_FEATURES 1
9 #include <xmlrpc-c/base.hpp>
10 #include <xmlrpc-c/client_simple.hpp>
11 #include <xmlrpc-c/girerr.hpp>
12 #include <xmlrpc-c/registry.hpp>
13 #include <xmlrpc-c/server_abyss.hpp>
14 #undef _LIBCPP_ENABLE_CXX17_REMOVED_FEATURES
15 #pragma GCC diagnostic pop
16 #include <cstdint>
17 #include <iostream>
18 #include <limits>
19 #include <memory>
20 #include <stdexcept>
21 #include "artdaq/DAQdata/Globals.hh"
22 #define TRACE_NAME (app_name + "_xmlrpc_commander").c_str()
23 #include "tracemf.h"
24 
25 #include <errno.h>
26 #include <netinet/in.h>
27 #include <sys/socket.h>
28 #include <cstring>
29 #include <exception>
30 #include "artdaq-core/Utilities/ExceptionHandler.hh"
31 
32 #include "canvas/Persistency/Provenance/RunID.h"
33 #include "fhiclcpp/make_ParameterSet.h"
34 
35 #include "artdaq/ExternalComms/xmlrpc_commander.hh"
36 //#include "artdaq/Application/LoadParameterSet.hh"
37 
38 namespace {
42 class env_wrap
43 {
44 public:
45  env_wrap() { xmlrpc_env_init(&this->env_c); };
46  ~env_wrap() { xmlrpc_env_clean(&this->env_c); };
47  xmlrpc_env env_c;
48 };
49 } // namespace
50 static xmlrpc_c::paramList
51 pListFromXmlrpcArray(xmlrpc_value* const arrayP)
52 {
53  env_wrap env;
54  XMLRPC_ASSERT_ARRAY_OK(arrayP);
55  unsigned int const arraySize = xmlrpc_array_size(&env.env_c, arrayP);
56  assert(!env.env_c.fault_occurred);
57  xmlrpc_c::paramList paramList(arraySize);
58  for (unsigned int i = 0; i < arraySize; ++i)
59  {
60  xmlrpc_value* arrayItemP;
61  xmlrpc_array_read_item(&env.env_c, arrayP, i, &arrayItemP);
62  assert(!env.env_c.fault_occurred);
63  paramList.add(xmlrpc_c::value(arrayItemP));
64  xmlrpc_DECREF(arrayItemP);
65  }
66  return paramList;
67 }
68 static xmlrpc_value*
69 c_executeMethod(xmlrpc_env* const envP,
70  xmlrpc_value* const paramArrayP,
71  void* const methodPtr,
72  void* const callInfoPtr)
73 {
74  xmlrpc_c::method* const methodP(static_cast<xmlrpc_c::method*>(methodPtr));
75  xmlrpc_c::paramList const paramList(pListFromXmlrpcArray(paramArrayP));
76  xmlrpc_c::callInfo* const callInfoP(static_cast<xmlrpc_c::callInfo*>(callInfoPtr));
77  xmlrpc_value* retval;
78  retval = NULL; // silence used-before-set warning
79  try
80  {
81  xmlrpc_c::value result;
82  try
83  {
84  xmlrpc_c::method2* const method2P(dynamic_cast<xmlrpc_c::method2*>(methodP));
85  if (method2P)
86  method2P->execute(paramList, callInfoP, &result);
87  else
88  methodP->execute(paramList, &result);
89  }
90  catch (xmlrpc_c::fault const& fault)
91  {
92  xmlrpc_env_set_fault(envP, fault.getCode(),
93  fault.getDescription().c_str());
94  }
95  if (!envP->fault_occurred)
96  {
97  if (result.isInstantiated())
98  retval = result.cValue();
99  else
100  girerr::throwf(
101  "Xmlrpc-c user's xmlrpc_c::method object's "
102  "'execute method' failed to set the RPC result "
103  "value.");
104  }
105  }
106  catch (std::exception const& e)
107  {
108  xmlrpc_faultf(envP,
109  "Unexpected error executing code for "
110  "particular method, detected by Xmlrpc-c "
111  "method registry code. Method did not "
112  "fail; rather, it did not complete at all. %s",
113  e.what());
114  }
115  catch (...)
116  {
117  xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR,
118  "Unexpected error executing code for "
119  "particular method, detected by Xmlrpc-c "
120  "method registry code. Method did not "
121  "fail; rather, it did not complete at all.");
122  }
123  return retval;
124 }
125 
126 namespace artdaq {
133 std::string exception_msg(const std::runtime_error& er,
134  const std::string& helpText = "execute request")
135 {
136  std::string msg("Exception when trying to ");
137  msg.append(helpText);
138  msg.append(": ");
139  msg.append(er.what()); //std::string(er.what ()).substr (2);
140  if (msg[msg.size() - 1] == '\n') msg.erase(msg.size() - 1);
141  return msg;
142 }
143 
150 std::string exception_msg(const art::Exception& er,
151  const std::string& helpText)
152 {
153  std::string msg("Exception when trying to ");
154  msg.append(helpText);
155  msg.append(": ");
156  msg.append(er.what());
157  if (msg[msg.size() - 1] == '\n') msg.erase(msg.size() - 1);
158  return msg;
159 }
160 
167 std::string exception_msg(const cet::exception& er,
168  const std::string& helpText)
169 {
170  std::string msg("Exception when trying to ");
171  msg.append(helpText);
172  msg.append(": ");
173  msg.append(er.what());
174  if (msg[msg.size() - 1] == '\n') msg.erase(msg.size() - 1);
175  return msg;
176 }
177 
184 std::string exception_msg(const std::string& erText,
185  const std::string& helpText)
186 {
187  std::string msg("Exception when trying to ");
188  msg.append(helpText);
189  msg.append(": ");
190  msg.append(erText);
191  if (msg[msg.size() - 1] == '\n') msg.erase(msg.size() - 1);
192  return msg;
193 }
194 
212 class cmd_ : public xmlrpc_c::method
213 {
214 public:
215  // Can't seem to initialize "_signature" and "_help" in the initialization list...
222  cmd_(xmlrpc_commander& c, const std::string& signature, const std::string& description)
223  : _c(c)
224  {
225  _signature = signature;
226  _help = description;
227  }
228 
234  void execute(const xmlrpc_c::paramList& paramList, xmlrpc_c::value* const retvalP) final;
235 
236 protected:
238 
244  virtual bool execute_(const xmlrpc_c::paramList&, xmlrpc_c::value* const retvalP) = 0;
245 
255  template<typename T>
256  T getParam(const xmlrpc_c::paramList& paramList, int index);
257 
281  template<typename T>
282  T getParam(const xmlrpc_c::paramList& paramList, int index, T default_value);
283 };
284 
285 // Users are only allowed to call getParam for predefined types; see
286 // template specializations below this default function
287 
288 template<typename T>
289 T cmd_::getParam(const xmlrpc_c::paramList&, int)
290 {
291  throw cet::exception("cmd_") << "Error in cmd_::getParam(): value type not supported" << std::endl;
292 }
293 
302 template<>
303 uint64_t cmd_::getParam<uint64_t>(const xmlrpc_c::paramList& paramList, int index)
304 {
305  TLOG(TLVL_TRACE) << "Getting parameter " << index << " from list as uint64_t.";
306  TLOG(TLVL_TRACE) << "Param value: " << paramList.getString(index);
307  return boost::lexical_cast<uint64_t>(paramList.getString(index));
308 }
309 
318 template<>
319 std::string cmd_::getParam<std::string>(const xmlrpc_c::paramList& paramList, int index)
320 {
321  TLOG(TLVL_TRACE) << "Getting parameter " << index << " from list as string.";
322  TLOG(TLVL_TRACE) << "Param value: " << paramList.getString(index);
323  return static_cast<std::string>(paramList.getString(index));
324 }
325 
334 template<>
335 art::RunID cmd_::getParam<art::RunID>(const xmlrpc_c::paramList& paramList, int index)
336 {
337  TLOG(TLVL_TRACE) << "Getting parameter " << index << " from list as Run Number.";
338  TLOG(TLVL_TRACE) << "Param value: " << paramList.getString(index);
339  std::string run_number_string = paramList.getString(index);
340  art::RunNumber_t run_number =
341  boost::lexical_cast<art::RunNumber_t>(run_number_string);
342  art::RunID run_id(run_number);
343 
344  return run_id;
345 }
346 
355 template<>
356 fhicl::ParameterSet cmd_::getParam<fhicl::ParameterSet>(const xmlrpc_c::paramList& paramList, int index)
357 {
358  TLOG(TLVL_TRACE) << "Getting parameter " << index << " from list as ParameterSet.";
359  TLOG(TLVL_TRACE) << "Param value: " << paramList.getString(index);
360  std::string configString = std::string(paramList.getString(index).c_str());
361  TLOG(TLVL_DEBUG) << "Loading Parameter Set from string: " << configString << std::endl;
362  fhicl::ParameterSet pset;
363 
364  try
365  {
366  fhicl::make_ParameterSet(configString, pset);
367  }
368  catch (const fhicl::exception& e)
369  {
370  if (getenv("FHICL_FILE_PATH") == nullptr)
371  {
372  std::cerr << "INFO: environment variable FHICL_FILE_PATH was not set. Using \".\"\n";
373  setenv("FHICL_FILE_PATH", ".", 0);
374  }
375  cet::filepath_lookup_after1 lookup_policy("FHICL_FILE_PATH");
376  fhicl::make_ParameterSet(configString, lookup_policy, pset);
377  }
378 
379  TLOG(TLVL_INFO) << "Parameter Set Loaded." << std::endl;
380  return pset;
381 }
382 
383 template<typename T>
384 T cmd_::getParam(const xmlrpc_c::paramList& paramList, int index,
385  T default_value)
386 {
387  T val = default_value;
388 
389  try
390  {
391  val = getParam<T>(paramList, index);
392  }
393  catch (const cet::exception& exception)
394  {
395  throw exception;
396  }
397  catch (...)
398  {}
399 
400  return val;
401 }
402 
403 void cmd_::execute(const xmlrpc_c::paramList& paramList, xmlrpc_c::value* const retvalP)
404 {
405  std::unique_lock<std::timed_mutex> lk(_c.mutex_, std::chrono::milliseconds(250));
406  if (lk.owns_lock())
407  {
408  try
409  {
410  // JCF, 9/4/14
411 
412  // Assuming the execute_ function returns true, then if the
413  // retvalP argument was untouched, assign it the string
414  // "Success"
415 
416  // See
417  // http://xmlrpc-c.sourceforge.net/doc/libxmlrpc++.html#isinstantiated
418  // for more on the concept of instantiation in xmlrpc_c::value objects
419 
420  if (execute_(paramList, retvalP))
421  {
422  if (!retvalP->isInstantiated())
423  {
424  *retvalP = xmlrpc_c::value_string("Success");
425  }
426  }
427  else
428  {
429  std::string problemReport = _c._commandable.report("transition_status");
430  *retvalP = xmlrpc_c::value_string(problemReport);
431  }
432  }
433  catch (std::runtime_error& er)
434  {
435  std::string msg = exception_msg(er, _help);
436  *retvalP = xmlrpc_c::value_string(msg);
437  TLOG(TLVL_ERROR) << msg;
438  }
439  catch (art::Exception& er)
440  {
441  std::string msg = exception_msg(er, _help);
442  *retvalP = xmlrpc_c::value_string(msg);
443  TLOG(TLVL_ERROR) << msg;
444  }
445  catch (cet::exception& er)
446  {
447  std::string msg = exception_msg(er, _help);
448  *retvalP = xmlrpc_c::value_string(msg);
449  TLOG(TLVL_ERROR) << msg;
450  }
451  catch (...)
452  {
453  std::string msg = exception_msg("Unknown exception", _help);
454  *retvalP = xmlrpc_c::value_string(msg);
455  TLOG(TLVL_ERROR) << msg;
456  }
457  }
458  else
459  {
460  *retvalP = xmlrpc_c::value_string("busy");
461  }
462 }
463 
465 
466 // JCF, 9/5/14
467 
468 // The three "init" transitions all take a FHiCL parameter list, and
469 // optionally a timeout and a timestamp; thus we can kill three birds
470 // with one stone in the GENERATE_INIT_TRANSITION macro
471 
472 #define GENERATE_INIT_TRANSITION(NAME, CALL, DESCRIPTION) \
473  \
474  class NAME##_ : public cmd_ \
475  { \
476  public: \
477  \
480  explicit NAME##_(xmlrpc_commander& c) : cmd_(c, "s:sii", DESCRIPTION) {} \
481  \
482  \
483  static const uint64_t defaultTimeout = 45; \
484  \
485  static const uint64_t defaultTimestamp = std::numeric_limits<const uint64_t>::max(); \
486  \
487  private: \
488  bool execute_(const xmlrpc_c::paramList& paramList, xmlrpc_c::value* const retvalP) \
489  { \
490  fhicl::ParameterSet ps; \
491  try \
492  { \
493  ps = getParam<fhicl::ParameterSet>(paramList, 0); \
494  } \
495  catch (...) \
496  { \
497  *retvalP = xmlrpc_c::value_string("The " #NAME " message requires a single argument that is a string containing the initialization ParameterSet"); \
498  return true; \
499  } \
500  \
501  return _c._commandable.CALL(ps, \
502  getParam<uint64_t>(paramList, 1, defaultTimeout), \
503  getParam<uint64_t>(paramList, 2, defaultTimestamp)); \
504  } \
505  };
506 
507 GENERATE_INIT_TRANSITION(init, initialize, "initialize the program")
508 
509 GENERATE_INIT_TRANSITION(soft_init, soft_initialize, "initialize software components in the program")
510 
511 GENERATE_INIT_TRANSITION(reinit, reinitialize, "re-initialize the program")
512 
513 #undef GENERATE_INIT_TRANSITION
514 
516 
520 class start_ : public cmd_
521 {
522 public:
528  : cmd_(c, "s:iii", "start the run")
529  {}
530 
532  static const uint64_t defaultTimeout = 45;
534  static const uint64_t defaultTimestamp = std::numeric_limits<const uint64_t>::max();
535 
536 private:
537  bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP) override
538  {
539  try
540  {
541  getParam<art::RunID>(paramList, 0);
542  }
543  catch (...)
544  {
545  *retvalP = xmlrpc_c::value_string("The start message requires the run number as an argument.");
546  return true;
547  }
548 
549  return _c._commandable.start(getParam<art::RunID>(paramList, 0),
550  getParam<uint64_t>(paramList, 1, defaultTimeout),
551  getParam<uint64_t>(paramList, 2, defaultTimestamp));
552  }
553 };
554 
556 
557 // JCF, 9/5/14
558 
559 // "pause", "resume" and "stop" all take an optional timeout and
560 // timestamp parameter, so we can generate them all with the
561 // GENERATE_TIMEOUT_TIMESTAMP_TRANSITION macro
562 
563 #define GENERATE_TIMEOUT_TIMESTAMP_TRANSITION(NAME, CALL, DESCRIPTION, TIMEOUT) \
564  \
565  class NAME##_ : public cmd_ \
566  { \
567  public: \
568  \
570  NAME##_(xmlrpc_commander& c) : cmd_(c, "s:ii", DESCRIPTION) {} \
571  \
572  \
573  static const uint64_t defaultTimeout = TIMEOUT; \
574  \
575  static const uint64_t defaultTimestamp = std::numeric_limits<const uint64_t>::max(); \
576  \
577  private: \
578  bool execute_(const xmlrpc_c::paramList& paramList, xmlrpc_c::value* const) \
579  { \
580  return _c._commandable.CALL(getParam<uint64_t>(paramList, 0, defaultTimeout), \
581  getParam<uint64_t>(paramList, 1, defaultTimestamp)); \
582  } \
583  };
584 
585 GENERATE_TIMEOUT_TIMESTAMP_TRANSITION(pause, pause, "pause the program", 45)
586 
587 GENERATE_TIMEOUT_TIMESTAMP_TRANSITION(resume, resume, "resume the program", 45)
588 
589 GENERATE_TIMEOUT_TIMESTAMP_TRANSITION(stop, stop, "stop the program", 45)
590 
591 #undef GENERATE_TIMEOUT_TIMESTAMP_TRANSITION
592 
596 class shutdown_ : public cmd_
597 {
598 public:
604  : cmd_(c, "s:i", "shutdown the program")
605  {}
606 
608  static const uint64_t defaultTimeout = 45;
609 
610 private:
611  bool execute_(const xmlrpc_c::paramList& paramList, xmlrpc_c::value* const)
612  {
613  auto ret = _c._commandable.shutdown(getParam<uint64_t>(paramList, 0, defaultTimeout));
614 
615 #if 1
616  if (_c.server) _c.server->terminate();
617 #endif
618 
619  return ret;
620  }
621 };
622 
626 class status_ : public cmd_
627 {
628 public:
634  : cmd_(c, "s:n", "report the current state")
635  {}
636 
637 private:
638  bool execute_(xmlrpc_c::paramList const&, xmlrpc_c::value* const retvalP)
639  {
640  *retvalP = xmlrpc_c::value_string(_c._commandable.status());
641  return true;
642  }
643 };
644 
648 class report_ : public cmd_
649 {
650 public:
656  : cmd_(c, "s:s", "report statistics")
657  {}
658 
659 private:
660  bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP)
661  {
662  try
663  {
664  getParam<std::string>(paramList, 0);
665  }
666  catch (...)
667  {
668  *retvalP = xmlrpc_c::value_string("The report message requires a single argument that selects the type of statistics to be reported.");
669  return true;
670  }
671 
672  *retvalP = xmlrpc_c::value_string(_c._commandable.report(getParam<std::string>(paramList, 0)));
673  return true;
674  }
675 };
676 
680 class legal_commands_ : public cmd_
681 {
682 public:
688  : cmd_(c, "s:n", "return the currently legal commands")
689  {}
690 
691 private:
692  bool execute_(xmlrpc_c::paramList const&, xmlrpc_c::value* const retvalP)
693  {
694  std::vector<std::string> cmdList = _c._commandable.legal_commands();
695  std::string resultString;
696 
697  for (auto& cmd : cmdList)
698  {
699  resultString.append(cmd + " ");
700  if (cmd == "shutdown")
701  {
702  resultString.append(" reset");
703  }
704  }
705  *retvalP = xmlrpc_c::value_string(resultString);
706 
707  return true;
708  }
709 };
710 
714 class register_monitor_ : public cmd_
715 {
716 public:
722  : cmd_(c, "s:s", "Get notified of a new monitor")
723  {}
724 
725 private:
726  bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP)
727  {
728  try
729  {
730  getParam<fhicl::ParameterSet>(paramList, 0);
731  }
732  catch (...)
733  {
734  *retvalP = xmlrpc_c::value_string("The register_monitor command expects a string representing the FHiCL definition of a Transfer plugin");
735  return true;
736  }
737 
738  *retvalP = xmlrpc_c::value_string(_c._commandable.register_monitor(getParam<fhicl::ParameterSet>(paramList, 0)));
739  return true;
740  }
741 };
742 
746 class unregister_monitor_ : public cmd_
747 {
748 public:
754  : cmd_(c, "s:s", "Remove a monitor")
755  {}
756 
757 private:
758  bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP)
759  {
760  try
761  {
762  getParam<std::string>(paramList, 0);
763  }
764  catch (...)
765  {
766  *retvalP = xmlrpc_c::value_string("The unregister_monitor command expects a string representing the label of the monitor to be removed");
767  return true;
768  }
769 
770  *retvalP = xmlrpc_c::value_string(_c._commandable.unregister_monitor(getParam<std::string>(paramList, 0)));
771  return true;
772  }
773 };
774 
778 class trace_set_ : public cmd_
779 {
780 public:
786  : cmd_(c, "s:ssi", "Set TRACE mask")
787  {}
788 
789 private:
790  bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP)
791  {
792  try
793  {
794  getParam<std::string>(paramList, 0);
795  getParam<std::string>(paramList, 1);
796  getParam<uint64_t>(paramList, 2);
797  }
798  catch (...)
799  {
800  *retvalP = xmlrpc_c::value_string("The trace_set command expects a mask type (M, S , or T), a name (ALL for all) and a mask");
801  return true;
802  }
803 
804  return _c._commandable.do_trace_set(getParam<std::string>(paramList, 0), getParam<std::string>(paramList, 1), getParam<uint64_t>(paramList, 2));
805  }
806 };
807 
811 class trace_get_ : public cmd_
812 {
813 public:
819  : cmd_(c, "s:s", "Get TRACE mask")
820  {}
821 
822 private:
823  bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP)
824  {
825  try
826  {
827  getParam<std::string>(paramList, 0);
828  }
829  catch (...)
830  {
831  *retvalP = xmlrpc_c::value_string("The trace_msgfacility_set command expects a name (ALL for all)");
832  return true;
833  }
834 
835  *retvalP = xmlrpc_c::value_string(_c._commandable.do_trace_get(getParam<std::string>(paramList, 0)));
836  return true;
837  }
838 };
839 
843 class meta_command_ : public cmd_
844 {
845 public:
851  : cmd_(c, "s:ss", "Run custom command")
852  {}
853 
854 private:
855  bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP)
856  {
857  try
858  {
859  getParam<std::string>(paramList, 0);
860  getParam<std::string>(paramList, 1);
861  }
862  catch (...)
863  {
864  *retvalP = xmlrpc_c::value_string("The meta_command command expects a string command and a string argument");
865  return true;
866  }
867 
868  return _c._commandable.do_meta_command(getParam<std::string>(paramList, 0), getParam<std::string>(paramList, 1));
869  }
870 };
871 
875 class rollover_subrun_ : public cmd_
876 {
877 public:
883  : cmd_(c, "s:ii", "create a new subrun")
884  {}
885 
886  static const uint64_t defaultSequenceID = 0xFFFFFFFFFFFFFFFF;
887  static const uint32_t defaultSubrunNumber = 1;
888 
889 private:
890  bool execute_(const xmlrpc_c::paramList& paramList, xmlrpc_c::value* const)
891  {
892  auto ret = _c._commandable.do_rollover_subrun(getParam<uint64_t>(paramList, 0, defaultSequenceID), getParam<uint32_t>(paramList, 1, defaultSubrunNumber));
893  return ret;
894  }
895 };
896 
901 {
902 public:
908  : cmd_(c, "s:ss", "Add an entry to the configuration archive list")
909  {}
910 
911 private:
912  bool execute_(xmlrpc_c::paramList const& paramList, xmlrpc_c::value* const retvalP)
913  {
914  try
915  {
916  getParam<std::string>(paramList, 0);
917  getParam<std::string>(paramList, 1);
918  }
919  catch (...)
920  {
921  *retvalP = xmlrpc_c::value_string("The add_config_archive_entry command expects a string key and a string value");
922  return true;
923  }
924 
925  return _c._commandable.do_add_config_archive_entry(getParam<std::string>(paramList, 0), getParam<std::string>(paramList, 1));
926  }
927 };
928 
933 {
934 public:
940  : cmd_(c, "s:n", "Clear the configuration archive list")
941  {}
942 
943 private:
944  bool execute_(xmlrpc_c::paramList const&, xmlrpc_c::value* const)
945  {
947  }
948 };
949 
950 // JCF, 9/4/14
951 
952 // Not sure if anyone was planning to resurrect this code by changing
953 // the preprocessor decision; as such, I'll leave it in for now...
954 
955 #if 0
956  class shutdown_ : public xmlrpc_c::registry::shutdown
957  {
958  public:
959  shutdown_(xmlrpc_c::serverAbyss *server) : _server(server) {}
960 
961  virtual void doit(const std::string& paramString, void*) const
962  {
963  TLOG(TLVL_INFO) << "A shutdown command was sent "
964  << "with parameter "
965  << paramString << "\"";
966  _server->terminate();
967  }
968  private:
969  xmlrpc_c::serverAbyss *_server;
970  };
971 #endif
972 
973 xmlrpc_commander::xmlrpc_commander(fhicl::ParameterSet ps, artdaq::Commandable& commandable)
974  : CommanderInterface(ps, commandable)
975  , port_(ps.get<int>("id", 0))
976  , serverUrl_(ps.get<std::string>("server_url", ""))
977  , server(nullptr)
978 {
979  if (serverUrl_ == "")
980  {
981  char hostname[HOST_NAME_MAX];
982  gethostname(hostname, HOST_NAME_MAX);
983  serverUrl_ = std::string(hostname);
984  }
985  if (serverUrl_.find("http") == std::string::npos)
986  {
987  serverUrl_ = "http://" + serverUrl_;
988  }
989  if (serverUrl_.find(std::to_string(port_)) == std::string::npos && serverUrl_.find(':', 7) == std::string::npos)
990  {
991  serverUrl_ = serverUrl_ + ":" + std::to_string(port_);
992  }
993  if (serverUrl_.find("RPC2") == std::string::npos)
994  {
995  serverUrl_ = serverUrl_ + "/RPC2";
996  }
997  TLOG(TLVL_INFO) << "XMLRPC COMMANDER CONSTRUCTOR: Port: " << port_ << ", Server Url: " << serverUrl_;
998 }
999 
1001 {
1002  //std::cout << "XMLRPC_COMMANDER RUN_SERVER CALLED!" << std::endl;
1003  xmlrpc_c::registry registry;
1004  struct xmlrpc_method_info3 methodInfo;
1005  memset(&methodInfo, 0, sizeof(methodInfo));
1006 
1007  /*#define register_method(m) \
1008  // xmlrpc_c::methodPtr const ptr_ ## m(new m ## _(*this));\
1009  registry.addMethod ("daq." #m, ptr_ ## m) */
1010 #define register_method(m) register_method2(m, 0x400000)
1011 
1012  xmlrpc_env env; // xmlrpc_env_init(&env);
1013  xmlrpc_registry*** c_registryPPP;
1014  c_registryPPP = (xmlrpc_registry***)(((char*)&registry) + sizeof(girmem::autoObject));
1015 
1016 #define register_method2(m, ss) \
1017  xmlrpc_c::method* ptr_##m(dynamic_cast<xmlrpc_c::method*>(new m##_(*this))); \
1018  std::string m##signature = ptr_##m->signature(), m##help = ptr_##m->help(); \
1019  methodInfo.methodName = "daq." #m; \
1020  methodInfo.methodFunction = &c_executeMethod; \
1021  methodInfo.serverInfo = ptr_##m; \
1022  methodInfo.stackSize = ss; \
1023  methodInfo.signatureString = &m##signature[0]; \
1024  methodInfo.help = &m##help[0]; \
1025  xmlrpc_env_init(&env); \
1026  xmlrpc_registry_add_method3(&env, **c_registryPPP, &methodInfo); \
1027  if (env.fault_occurred) throw(girerr::error(env.fault_string)); \
1028  xmlrpc_env_clean(&env)
1029 
1030  register_method2(init, 0x200000);
1031  register_method(soft_init);
1032  register_method(reinit);
1033  register_method(start);
1034  register_method(status);
1035  register_method(report);
1036  register_method(stop);
1037  register_method(pause);
1038  register_method(resume);
1039  register_method(register_monitor);
1040  register_method(unregister_monitor);
1041  register_method(legal_commands);
1042  register_method(trace_set);
1043  register_method(trace_get);
1044  register_method(meta_command);
1045  register_method(rollover_subrun);
1046  register_method(add_config_archive_entry);
1047  register_method(clear_config_archive);
1048 
1049  register_method(shutdown);
1050 
1051  // alias "daq.reset" to the internal shutdown transition
1052  xmlrpc_c::methodPtr const ptr_reset(new shutdown_(*this));
1053  registry.addMethod("daq.reset", ptr_reset);
1054 
1055 #undef register_method
1056 
1057  // JCF, 6/3/15
1058 
1059  // In the following code, I configure a socket to have the
1060  // SO_REUSEADDR option so that once an artdaq process closes, the
1061  // port it was communicating on becomes immediately available
1062  // (desirable if, say, the DAQ program is terminated and then
1063  // immediately restarted)
1064 
1065  // Much of the following code is cribbed from
1066  // http://fossies.org/linux/freeswitch/libs/xmlrpc-c/src/cpp/test/server_abyss.cpp
1067 
1068  // Below, "0" is the default protocol (in this case, given the IPv4
1069  // Protocol Family (PF_INET) and the SOCK_STREAM communication
1070  // method)
1071 
1072  XMLRPC_SOCKET socket_file_descriptor = socket(PF_INET, SOCK_STREAM, 0);
1073 
1074  if (socket_file_descriptor < 0)
1075  {
1076  throw cet::exception("xmlrpc_commander::run") << "Problem with the socket() call; C-style errno == " << errno << " (" << strerror(errno) << ")";
1077  }
1078 
1079  int enable = 1;
1080  int retval = setsockopt(socket_file_descriptor,
1081  SOL_SOCKET, SO_REUSEADDR,
1082  &enable, sizeof(int));
1083 
1084  if (retval < 0)
1085  {
1086  throw cet::exception("xmlrpc_commander::run") << "Problem with the call to setsockopt(); C-style errno == " << errno << " (" << strerror(errno) << ")";
1087  }
1088 
1089  struct sockaddr_in sockAddr;
1090 
1091  sockAddr.sin_family = AF_INET;
1092  sockAddr.sin_port = htons(port_);
1093  sockAddr.sin_addr.s_addr = 0;
1094 
1095  retval = bind(socket_file_descriptor,
1096  reinterpret_cast<struct sockaddr*>(&sockAddr),
1097  sizeof(sockAddr));
1098 
1099  if (retval != 0)
1100  {
1101  close(socket_file_descriptor);
1102  throw cet::exception("xmlrpc_commander::run") << "Problem with the bind() call; C-style errno == " << errno << " (" << strerror(errno) << ")";
1103  }
1104 
1105  server.reset(new xmlrpc_c::serverAbyss(xmlrpc_c::serverAbyss::constrOpt().registryP(&registry).socketFd(socket_file_descriptor)));
1106 
1107 #if 0
1108  xmlrpc_c::serverAbyss::shutdown shutdown_obj(&server);
1109  registry.setShutdown(&shutdown_obj);
1110 #endif
1111 
1112  TLOG(TLVL_DEBUG) << "running server";
1113 
1114  // JCF, 6/3/15
1115 
1116  // Use a catch block to clean up (i.e., close the socket). An
1117  // opportunity for RAII, although all control paths are limited to
1118  // this section of the file...
1119 
1120  try
1121  {
1122  running_ = true;
1123  server->run();
1124  running_ = false;
1125  }
1126  catch (...)
1127  {
1128  TLOG(TLVL_WARNING) << "server threw an exception; closing the socket and rethrowing";
1129  running_ = false;
1130  close(socket_file_descriptor);
1131  throw;
1132  }
1133 
1134  close(socket_file_descriptor);
1135  TLOG(TLVL_DEBUG) << "server terminated";
1136 }
1137 catch (...)
1138 {
1139  throw;
1140 }
1141 
1142 std::string xmlrpc_commander::send_command_(std::string command)
1143 {
1144  if (serverUrl_ == "")
1145  {
1146  std::stringstream errmsg;
1147  errmsg << "Problem attempting " << command << " XML-RPC call: No server URL set!";
1148  ExceptionHandler(ExceptionHandlerRethrow::yes, errmsg.str());
1149  }
1150  xmlrpc_c::clientSimple myClient;
1151  xmlrpc_c::value result;
1152 
1153  try
1154  {
1155  myClient.call(serverUrl_, "daq." + command, "", &result);
1156  }
1157  catch (...)
1158  {
1159  std::stringstream errmsg;
1160  errmsg << "Problem attempting " << command << " XML-RPC call on host " << serverUrl_
1161  << "; possible causes are malformed FHiCL or nonexistent process at requested port";
1162  ExceptionHandler(ExceptionHandlerRethrow::yes, errmsg.str());
1163  }
1164 
1165  return xmlrpc_c::value_string(result);
1166 }
1167 
1168 std::string xmlrpc_commander::send_command_(std::string command, std::string arg)
1169 {
1170  if (serverUrl_ == "")
1171  {
1172  std::stringstream errmsg;
1173  errmsg << "Problem attempting " << command << " XML-RPC call: No server URL set!";
1174  ExceptionHandler(ExceptionHandlerRethrow::yes, errmsg.str());
1175  }
1176  xmlrpc_c::clientSimple myClient;
1177  xmlrpc_c::value result;
1178 
1179  try
1180  {
1181  myClient.call(serverUrl_, "daq." + command, "s", &result, arg.c_str());
1182  }
1183  catch (...)
1184  {
1185  std::stringstream errmsg;
1186  errmsg << "Problem attempting " << command << " XML-RPC call on host " << serverUrl_
1187  << "; possible causes are malformed FHiCL or nonexistent process at requested port";
1188  ExceptionHandler(ExceptionHandlerRethrow::yes, errmsg.str());
1189  }
1190 
1191  return xmlrpc_c::value_string(result);
1192 }
1193 
1194 std::string xmlrpc_commander::send_command_(std::string command, fhicl::ParameterSet pset, uint64_t timestamp, uint64_t timeout)
1195 {
1196  if (serverUrl_ == "")
1197  {
1198  std::stringstream errmsg;
1199  errmsg << "Problem attempting " << command << " XML-RPC call: No server URL set!";
1200  ExceptionHandler(ExceptionHandlerRethrow::yes, errmsg.str());
1201  }
1202  xmlrpc_c::clientSimple myClient;
1203  xmlrpc_c::value result;
1204 
1205  try
1206  {
1207  myClient.call(serverUrl_, "daq." + command, "sii", &result, pset.to_string().c_str(), timestamp, timeout);
1208  }
1209  catch (...)
1210  {
1211  std::stringstream errmsg;
1212  errmsg << "Problem attempting " << command << " XML-RPC call on host " << serverUrl_
1213  << "; possible causes are malformed FHiCL or nonexistent process at requested port";
1214  ExceptionHandler(ExceptionHandlerRethrow::yes, errmsg.str());
1215  }
1216 
1217  return xmlrpc_c::value_string(result);
1218 }
1219 
1220 std::string artdaq::xmlrpc_commander::send_command_(std::string command, uint64_t a, uint64_t b)
1221 {
1222  if (serverUrl_ == "")
1223  {
1224  std::stringstream errmsg;
1225  errmsg << "Problem attempting " << command << " XML-RPC call: No server URL set!";
1226  ExceptionHandler(ExceptionHandlerRethrow::yes, errmsg.str());
1227  }
1228  xmlrpc_c::clientSimple myClient;
1229  xmlrpc_c::value result;
1230 
1231  try
1232  {
1233  myClient.call(serverUrl_, "daq." + command, "ii", &result, a, b);
1234  }
1235  catch (...)
1236  {
1237  std::stringstream errmsg;
1238  errmsg << "Problem attempting " << command << " XML-RPC call on host " << serverUrl_
1239  << "; possible causes are malformed FHiCL or nonexistent process at requested port";
1240  ExceptionHandler(ExceptionHandlerRethrow::yes, errmsg.str());
1241  }
1242 
1243  return xmlrpc_c::value_string(result);
1244 }
1245 
1246 std::string artdaq::xmlrpc_commander::send_command_(std::string command, art::RunID r, uint64_t a, uint64_t b)
1247 {
1248  if (serverUrl_ == "")
1249  {
1250  std::stringstream errmsg;
1251  errmsg << "Problem attempting " << command << " XML-RPC call: No server URL set!";
1252  ExceptionHandler(ExceptionHandlerRethrow::yes, errmsg.str());
1253  }
1254  xmlrpc_c::clientSimple myClient;
1255  xmlrpc_c::value result;
1256 
1257  try
1258  {
1259  myClient.call(serverUrl_, "daq." + command, "iii", &result, r, a, b);
1260  }
1261  catch (...)
1262  {
1263  std::stringstream errmsg;
1264  errmsg << "Problem attempting " << command << " XML-RPC call on host " << serverUrl_
1265  << "; possible causes are malformed FHiCL or nonexistent process at requested port";
1266  ExceptionHandler(ExceptionHandlerRethrow::yes, errmsg.str());
1267  }
1268 
1269  return xmlrpc_c::value_string(result);
1270 }
1271 
1272 std::string artdaq::xmlrpc_commander::send_command_(std::string command, uint64_t arg1)
1273 {
1274  if (serverUrl_ == "")
1275  {
1276  std::stringstream errmsg;
1277  errmsg << "Problem attempting " << command << " XML-RPC call: No server URL set!";
1278  ExceptionHandler(ExceptionHandlerRethrow::yes, errmsg.str());
1279  }
1280  xmlrpc_c::clientSimple myClient;
1281  xmlrpc_c::value result;
1282 
1283  try
1284  {
1285  myClient.call(serverUrl_, "daq." + command, "i", &result, arg1);
1286  }
1287  catch (...)
1288  {
1289  std::stringstream errmsg;
1290  errmsg << "Problem attempting " << command << " XML-RPC call on host " << serverUrl_
1291  << "; possible causes are malformed FHiCL or nonexistent process at requested port";
1292  ExceptionHandler(ExceptionHandlerRethrow::yes, errmsg.str());
1293  }
1294 
1295  return xmlrpc_c::value_string(result);
1296 }
1297 
1298 std::string artdaq::xmlrpc_commander::send_command_(std::string command, std::string arg1, std::string arg2)
1299 {
1300  if (serverUrl_ == "")
1301  {
1302  std::stringstream errmsg;
1303  errmsg << "Problem attempting " << command << " XML-RPC call: No server URL set!";
1304  ExceptionHandler(ExceptionHandlerRethrow::yes, errmsg.str());
1305  }
1306  xmlrpc_c::clientSimple myClient;
1307  xmlrpc_c::value result;
1308 
1309  try
1310  {
1311  myClient.call(serverUrl_, "daq." + command, "ss", &result, arg1.c_str(), arg2.c_str());
1312  }
1313  catch (...)
1314  {
1315  std::stringstream errmsg;
1316  errmsg << "Problem attempting " << command << " XML-RPC call on host " << serverUrl_
1317  << "; possible causes are malformed FHiCL or nonexistent process at requested port";
1318  ExceptionHandler(ExceptionHandlerRethrow::yes, errmsg.str());
1319  }
1320 
1321  return xmlrpc_c::value_string(result);
1322 }
1323 
1324 std::string artdaq::xmlrpc_commander::send_command_(std::string command, std::string arg1, std::string arg2, uint64_t arg3)
1325 {
1326  if (serverUrl_ == "")
1327  {
1328  std::stringstream errmsg;
1329  errmsg << "Problem attempting " << command << " XML-RPC call: No server URL set!";
1330  ExceptionHandler(ExceptionHandlerRethrow::yes, errmsg.str());
1331  }
1332  xmlrpc_c::clientSimple myClient;
1333  xmlrpc_c::value result;
1334 
1335  try
1336  {
1337  myClient.call(serverUrl_, "daq." + command, "ssi", &result, arg1.c_str(), arg2.c_str(), arg3);
1338  }
1339  catch (...)
1340  {
1341  std::stringstream errmsg;
1342  errmsg << "Problem attempting " << command << " XML-RPC call on host " << serverUrl_
1343  << "; possible causes are malformed FHiCL or nonexistent process at requested port";
1344  ExceptionHandler(ExceptionHandlerRethrow::yes, errmsg.str());
1345  }
1346 
1347  return xmlrpc_c::value_string(result);
1348 }
1349 
1350 std::string xmlrpc_commander::send_register_monitor(std::string monitor_fhicl)
1351 {
1352  return send_command_("register_monitor", monitor_fhicl);
1353 }
1354 std::string xmlrpc_commander::send_unregister_monitor(std::string monitor_label)
1355 {
1356  return send_command_("unregister_monitor", monitor_label);
1357 }
1358 std::string artdaq::xmlrpc_commander::send_init(fhicl::ParameterSet ps, uint64_t a, uint64_t b)
1359 {
1360  return send_command_("init", ps, a, b);
1361 }
1362 std::string artdaq::xmlrpc_commander::send_soft_init(fhicl::ParameterSet ps, uint64_t a, uint64_t b)
1363 {
1364  return send_command_("soft_init", ps, a, b);
1365 }
1366 std::string xmlrpc_commander::send_reinit(fhicl::ParameterSet ps, uint64_t a, uint64_t b)
1367 {
1368  return send_command_("reinit", ps, a, b);
1369 }
1370 std::string xmlrpc_commander::send_start(art::RunID r, uint64_t a, uint64_t b)
1371 {
1372  return send_command_("start", r, a, b);
1373 }
1374 std::string xmlrpc_commander::send_pause(uint64_t a, uint64_t b)
1375 {
1376  return send_command_("pause", a, b);
1377 }
1378 std::string xmlrpc_commander::send_resume(uint64_t a, uint64_t b)
1379 {
1380  return send_command_("resume", a, b);
1381 }
1382 std::string xmlrpc_commander::send_stop(uint64_t a, uint64_t b)
1383 {
1384  return send_command_("stop", a, b);
1385 }
1386 std::string xmlrpc_commander::send_shutdown(uint64_t a)
1387 {
1388  return send_command_("shutdown", a);
1389 }
1391 {
1392  return send_command_("status");
1393 }
1394 std::string xmlrpc_commander::send_report(std::string what)
1395 {
1396  return send_command_("report", what);
1397 }
1399 {
1400  return send_command_("legal_commands");
1401 }
1402 std::string xmlrpc_commander::send_trace_get(std::string name)
1403 {
1404  return send_command_("trace_get", name);
1405 }
1406 std::string xmlrpc_commander::send_trace_set(std::string name, std::string which, uint64_t mask)
1407 {
1408  return send_command_("trace_set", name, which, mask);
1409 }
1410 std::string xmlrpc_commander::send_meta_command(std::string command, std::string arg)
1411 {
1412  return send_command_("meta_command", command, arg);
1413 }
1414 std::string xmlrpc_commander::send_rollover_subrun(uint64_t when, uint32_t sr)
1415 {
1416  return send_command_("rollover_subrun", when, sr);
1417 }
1418 } // namespace artdaq
1419 
1420 DEFINE_ARTDAQ_COMMANDER(artdaq::xmlrpc_commander)
virtual bool do_add_config_archive_entry(std::string const &, std::string const &)
Add the specified key-value pair to the configuration archive list.
Definition: Commandable.cc:496
std::string send_stop(uint64_t, uint64_t) override
Send a stop command over XMLRPC
std::string send_shutdown(uint64_t) override
Send a shutdown command over XMLRPC
This interface defines the functions used to transfer data between artdaq applications.
cmd_(xmlrpc_commander &c, const std::string &signature, const std::string &description)
cmd_ Constructor
static const uint64_t defaultTimeout
std::string send_unregister_monitor(std::string monitor_label) override
Send an unregister_monitor command over XMLRPC
std::string send_register_monitor(std::string monitor_fhicl) override
Send a register_monitor command over XMLRPC
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
virtual bool do_clear_config_archive()
Clears the configuration archive list.
Definition: Commandable.cc:502
xmlrpc_commander(fhicl::ParameterSet ps, artdaq::Commandable &commandable)
xmlrpc_commander Constructor
Command class representing a start transition.
rollover_subrun_(xmlrpc_commander &c)
shutdown_ Constructor
std::string send_soft_init(fhicl::ParameterSet, uint64_t, uint64_t) override
Send a soft_init command over XMLRPC
status_ Command class
report_ Command class
std::string send_pause(uint64_t, uint64_t) override
Send a pause command over XMLRPC
virtual std::string clear_config_archive()
Using the transport mechanism, send a clear_config_archive command
virtual std::string unregister_monitor(std::string const &)
Perform the unregister_monitor action.
Definition: Commandable.hh:150
meta_command_ Command class
std::string send_status() override
Send a status command over XMLRPC
std::string send_legal_commands() override
Send a legal_commands command over XMLRPC
The xmlrpc_commander class serves as the XMLRPC server run in each artdaq application.
static const uint64_t defaultTimestamp
static const uint32_t defaultSubrunNumber
Default subrun number for command.
The &quot;cmd_&quot; class serves as the base class for all artdaq&#39;s XML-RPC commands.
trace_set_(xmlrpc_commander &c)
unregister_monitor_ Constructor
shutdown_ Command class
std::string send_report(std::string) override
Send a report command over XMLRPC
void run_server() override
Run the XMLRPC server.
meta_command_(xmlrpc_commander &c)
meta_command_ Constructor
std::string send_meta_command(std::string, std::string) override
Send an send_meta_command command over XMLRPC
std::unique_ptr< xmlrpc_c::serverAbyss > server
XMLRPC server.
std::string status() const
Returns the current state of the Commandable.
Definition: Commandable.cc:272
std::string exception_msg(const std::runtime_error &er, const std::string &helpText="execute request")
Write an exception message.
virtual bool do_trace_set(std::string const &type, std::string const &name, uint64_t mask)
Set the given TRACE mask for the given TRACE name.
Definition: Commandable.cc:433
static const uint64_t defaultSequenceID
Default Sequence ID for command.
add_config_archive_entry_(xmlrpc_commander &c)
add_config_archive_entry_ Constructor
virtual std::string register_monitor(fhicl::ParameterSet const &)
Perform the register_monitor action.
Definition: Commandable.hh:139
virtual bool do_meta_command(std::string const &command, std::string const &args)
Run a module-defined command with the given parameter string.
Definition: Commandable.cc:483
register_monitor_ Command class
add_config_archive_entry_ Command class
legal_commands_ Command class
artdaq::Commandable & _commandable
Reference to the Commandable that this Commander Commands.
std::string send_trace_get(std::string) override
Send an send_trace_get command over XMLRPC
std::string send_reinit(fhicl::ParameterSet, uint64_t, uint64_t) override
Send a reinit command over XMLRPC
xmlrpc_commander & _c
The xmlrpc_commander instance that the command will be sent to.
clear_config_archive_ Command class
trace_get_ Command class
std::string send_resume(uint64_t, uint64_t) override
Send a resume command over XMLRPC
status_(xmlrpc_commander &c)
status_ Constructor
clear_config_archive_(xmlrpc_commander &c)
clear_config_archive_ 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 ...
Wrapper for XMLRPC environment construction/destruction
std::timed_mutex mutex_
XMLRPC mutex.
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
std::string send_rollover_subrun(uint64_t, uint32_t) override
Send a send_rollover_subrun command over XMLRPC
std::vector< std::string > legal_commands() const
Get the legal transition commands from the current state.
Definition: Commandable.cc:285
std::string send_start(art::RunID, uint64_t, uint64_t) override
Send a start command over XMLRPC
rollover_subrun_ Command class
bool start(art::RunID id, uint64_t timeout, uint64_t timestamp)
Processes the start transition.
Definition: Commandable.cc:59
report_(xmlrpc_commander &c)
report_ Constructor
trace_set_ Command class
start_(xmlrpc_commander &c)
start_ Command (cmd_ derived class) Constructor
virtual std::string add_config_archive_entry(std::string, std::string)
Using the transport mechanism, send an add_config_archive_entry command
virtual std::string do_trace_get(std::string const &name)
Get the TRACE mask for the given TRACE name If name is &quot;ALL&quot;, then all TRACE masks will be printed...
Definition: Commandable.cc:404
std::string send_init(fhicl::ParameterSet, uint64_t, uint64_t) override
Send an init command over XMLRPC
trace_get_(xmlrpc_commander &c)
trace_msgfacility_set_ Constructor
std::string send_trace_set(std::string, std::string, uint64_t) override
Send an send_trace_msgfacility_set command over XMLRPC
std::atomic< bool > running_
Whether the server is running and able to respond to requests.
virtual bool do_rollover_subrun(uint64_t eventNum, uint32_t subrunNum)
Perform the rollover_subrun transition.
Definition: Commandable.cc:489
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:166