PMDA++  0.4.4
Header-only C++ library for writing PCP PMDAs
pmda.hpp
Go to the documentation of this file.
1 // Copyright Paul Colby 2013 - 2015.
2 // Distributed under the Boost Software License, Version 1.0.
3 // (See accompanying file LICENSE.md or copy at
4 // http://www.boost.org/LICENSE_1_0.txt)
5 
6 /**
7  * @file
8  * @brief Defines the pcp::pmda class.
9  */
10 
11 #ifndef __PCP_CPP_PMDA_HPP__
12 #define __PCP_CPP_PMDA_HPP__
13 
14 #include "config.hpp"
15 #include "exception.hpp"
16 #include "instance_domain.hpp"
17 #include "metric_description.hpp"
18 
19 #include <algorithm>
20 #include <fstream>
21 #include <iostream>
22 #include <set>
23 #include <sstream>
24 #include <stack>
25 #include <stdexcept>
26 #include <stdint.h>
27 #include <string>
28 #include <vector>
29 
30 PCP_CPP_BEGIN_NAMESPACE
31 
32 namespace pcp {
33 
34 /**
35  * @brief Abstract base class for implementing PCP PMDAs.
36  */
37 class pmda {
38 
39 public:
40 
41  /**
42  * @brief Initialize PCP's DSO interface for a PMDA.
43  *
44  * Example usage:
45  * @code
46  * class my_pmda : public pcp::pmda {
47  * ....
48  * };
49  *
50  * extern "C" void trivial_init(pmdaInterface *interface)
51  * {
52  * pcp::pmda::init_dso<my_pmda>(interface);
53  * }
54  * @endcode
55  *
56  * @param interface PMDA interface point provided by PCP as part of the DSO
57  * initialisation process.
58  */
59  template <class Agent>
60  static void init_dso(pmdaInterface * const interface)
61  {
62  try {
63  set_instance(new Agent);
64  get_instance()->initialize_dso(*interface);
65  } catch (const std::exception &ex) {
66  __pmNotifyErr(LOG_ERR, "%s", ex.what());
67  }
68  }
69 
70  /**
71  * @brief Run a PMDA in PCP's daemon mode.
72  *
73  * Example usage:
74  * @code
75  * class my_pmda : public pcp::pmda {
76  * ....
77  * };
78  *
79  * int main(int argc, char *argv[])
80  * {
81  * return pcp::pmda::run_daemon<my_pmda>(argc, argv);
82  * }
83  * @endcode
84  *
85  * @param argc Argument count.
86  * @param argv Argument vector.
87  *
88  * @return EXIT_SUCCESS on success, EXIT_FAILURE on error.
89  */
90  template <class Agent>
91  static int run_daemon(const int argc, char * const argv[])
92  {
93  try {
94  set_instance(new Agent);
95  get_instance()->run_daemon(argc, argv);
96  } catch (const std::exception &ex) {
97  __pmNotifyErr(LOG_ERR, "%s", ex.what());
98  delete set_instance(NULL);
99  return EXIT_FAILURE;
100  }
101  delete set_instance(NULL);
102  return EXIT_SUCCESS;
103  }
104 
105 protected:
106 
107  /// Description of all metrics supported by this PMDA. This is really just
108  /// a cache of the value returned by get_supported_metrics during startup.
110 
111  /**
112  * @brief Struct returned by the fetch_value function.
113  *
114  * @see pcp::fetch_value
115  */
117  pmAtomValue atom; ///< Atom value.
118  int code; ///< PMDA fetch code describing atom's memory allocation.
119 
120  /**
121  * @brief Constructor.
122  *
123  * @param atom PCP atom value.
124  * @param code Optional code to return to PCP. Should be one of the
125  * PMDA_FETCH_* constants.
126  */
127  fetch_value_result(const pmAtomValue &atom,
128  const int code = PMDA_FETCH_STATIC)
129  : atom(atom), code(code) { }
130  };
131 
132  /**
133  * @brief Indentifies a metric to be fetched.
134  *
135  * @see pcp::fetch_value
136  */
137  struct metric_id {
138  cluster_id_type cluster; ///< Cluster ID.
139  item_id_type item; ///< Item ID.
140  instance_id_type instance; ///< Instance ID.
141  atom_type_type type; ///< Expected atom type.
142  void * opaque; ///< Opaque pointer.
143  };
144 
145  /// @brief A simple vector of strings.
146  typedef std::vector<std::string> string_vector;
147 
148  /**
149  * @brief Destructor.
150  */
151  virtual ~pmda()
152  {
153  while (!free_on_destruction.empty()) {
154  free(free_on_destruction.top());
155  free_on_destruction.pop();;
156  }
157  }
158 
159  /**
160  * @brief Get the single PMDA instance.
161  *
162  * @return A pointer to the single PMDA instance, if set, otherwise \c NULL.
163  *
164  * @see set_instance
165  */
166  static pmda * get_instance() {
167  return instance;
168  }
169 
170  /**
171  * @brief Set the single PMDA instance.
172  *
173  * @note For performance reasons, this function is intentionally not thread
174  * safe. Nor does it need to be, given that the instance is set by
175  * either init_dso or run_daemon, long before any threads interested
176  * in this instance have been created.
177  *
178  * @param new_instance The new instance to set.
179  *
180  * @return A pointer to the previous instance, if there was one, otherwise
181  * \c NULL.
182  *
183  * @see get_instance
184  */
185  static pmda * set_instance(pmda * const new_instance) {
186  pmda * const old_instance = instance;
187  instance = new_instance;
188  return old_instance;
189  }
190 
191 #ifndef PCP_CPP_NO_BOOST
192  /**
193  * @brief Get the default path to this PMDA's optional configuration file.
194  *
195  * Derived classes may override this function to provide a custom path. The
196  * default is equivalent to $PCP_PMDAS_DIR/$PMDA_NAME/config.
197  *
198  * @note This function is only available if Boost support is enabled.
199  *
200  * @return The path to this PMDA's optional configuration file.
201  */
202  virtual std::string get_config_file_pathname() const
203  {
204  const std::string sep(1, __pmPathSeparator());
205  return pmGetConfig("PCP_PMDAS_DIR") + sep + get_pmda_name() + sep + "config";
206  }
207 #endif
208 
209  /**
210  * @brief Get the default path to this PMDA's optional help texts file.
211  *
212  * Derived classes may override this function to provide a custom path. The
213  * default is equivalent to $PCP_PMDAS_DIR/$PMDA_NAME/help.
214  *
215  * @return The path to this PMDA's optional help texts file.
216  */
217  virtual std::string get_help_text_pathname() const
218  {
219  const std::string sep(1, __pmPathSeparator());
220  return pmGetConfig("PCP_PMDAS_DIR") + sep + get_pmda_name() + sep + "help";
221  }
222 
223  /**
224  * @brief Get the default path to this PMDA's log file.
225  *
226  * Derived classes may override this function to provide a custom path. The
227  * default is equivalent to $PCP_PMCD_DIR/$PMDA_NAME.log.
228  *
229  * @return The path to this PMDA's log file.
230  */
231  virtual std::string get_log_file_pathname() const
232  {
233  return get_pmda_name() + ".log";
234  }
235 
236  /**
237  * @brief Get this PMDA's name.
238  *
239  * Derived classes must override this function to return the name of the
240  * PMDA.
241  *
242  * @return This PMDA's name.
243  */
244  virtual std::string get_pmda_name() const = 0;
245 
246  /**
247  * @brief Get this PMDA's default performance metrics domain number.
248  *
249  * Derived classes must override this function to return the default domain
250  * number for this PMDA.
251  *
252  * Unless the command line parsing functions are overridden, this class will
253  * allow this default PMDA domain number to be overridden via the -d or
254  * --domain (Boost only) command line options.
255  *
256  * @note The PCP project maintains a list of standard PMIDs. You should
257  * normally avoid clashing with any of those.
258  *
259  * @return This PMDA's default domain number.
260  *
261  * @see http://git.pcp.io/cgi-bin/gitweb.cgi?p=pcp/pcp.git;a=blob;f=src/pmns/stdpmid.pcp
262  */
263  virtual int get_default_pmda_domain_number() const = 0;
264 
265  /**
266  * @brief Get this PMDA's version string.
267  *
268  * Derived classes may override this function to return a custom version
269  * string to be included in messages such as the outpt of the --version
270  * command line option.
271  *
272  * This base implementation returns an empty string.
273  *
274  * @return This PMDA's version string.
275  */
276  virtual std::string get_pmda_version() const
277  {
278  return std::string();
279  }
280 
281  /**
282  * @brief Run this daemon.
283  *
284  * This function implements the main processing loop of the daemon-mode
285  * PMDA. It performs various initalisations such as parsing the command
286  * line options, then defers processing to the pmdaMain function.
287  *
288  * @param argc Argument count.
289  * @param argv Argument vector.
290  *
291  * @throw pcp::exception On error.
292  */
293  virtual void run_daemon(const int argc, char * const argv[])
294  {
295  // Create some local strings. We keep these as separate variables
296  // (as opposed to using function call results directly) because PMAPI
297  // functions will copy their pointers (not their contents), so the
298  // pointers must remain valid for the life of this function.
299  const std::string program_name = get_pmda_name();
300  const std::string help_text_pathname = get_help_text_pathname();
301  const std::string log_file_pathname = get_log_file_pathname();
302 
303  // Set the program name.
304  __pmSetProgname(program_name.c_str());
305 
306  // Initialize the PMDA to run as a daemon.
307  pmdaInterface interface;
308  // PCP 3.11.5 updated the pmdaDaemon signature to use const char pointers.
309  // So for earlier versions only, we need to cast away some constness.
310  // Note, the PM_VERSION* macros themselves weren't added until PCP 3.10.5.
311  #if !defined PM_VERSION_CURRENT || PM_VERSION_CURRENT < 0x30B05 // 3.11.5
312  #define PCP_CPP_PMDA_CONST_CHAR(str) const_cast<char *>(str)
313  #else
314  #define PCP_CPP_PMDA_CONST_CHAR(str) str // Do nothing.
315  #endif
316  pmdaDaemon(&interface, PCP_CPP_PMDA_INTERFACE_VERSION,
317  PCP_CPP_PMDA_CONST_CHAR(program_name.c_str()),
319  PCP_CPP_PMDA_CONST_CHAR(log_file_pathname.c_str()),
320  (help_text_pathname.empty()) ? NULL : PCP_CPP_PMDA_CONST_CHAR(help_text_pathname.c_str())
321  );
322  #undef PCP_CPP_PMDA_CONST_CHAR
323 
324  // Parse the command line options.
325  if (!parse_command_line(argc, argv, interface)) {
326  // The parse function already did whatever the command line asked
327  // for (eg --help, or --export-pmns), so we're done now.
328  return;
329  }
330 
331  // Redirect output the log.
332  pmdaOpenLog(&interface);
333 
334  // Initialize the rest of the PMDA.
335  initialize_pmda(interface);
336 
337  // Establish a connection between this daemon PMDA and PMCD.
338  pmdaConnect(&interface);
339 
340  // Run the generic PDU processing loop.
341  pmdaMain(&interface);
342 
343  // Free the instance domains and metrics allocated in initialize_pmda.
344  for (int index = 0; index < interface.version.two.ext->e_nindoms; ++index) {
345  delete [] interface.version.two.ext->e_indoms[index].it_set;
346  }
347  delete[] interface.version.two.ext->e_indoms;
348  delete[] interface.version.two.ext->e_metrics;
349  }
350 
351 #ifdef PCP_CPP_NO_BOOST
352  /**
353  * @brief Parse command line options.
354  *
355  * This non-Boost version uses PCP's pmdaGetOpt function (a wrapper for the
356  * POSIX getopt function, with a number of built-in options) to parse the
357  * command line options.
358  *
359  * @param argc Argument count.
360  * @param argv Argumnet vector.
361  * @param interface PMDA interface.
362  *
363  * @return \c true if the caller should continue to run the PMDA, \c false
364  * on error.
365  */
366  virtual bool parse_command_line(const int argc, const char * const argv[],
367  pmdaInterface& interface)
368  {
369  int c, error_count = 0;
370  while ((c = pmdaGetOpt(argc, const_cast<char **>(argv), get_supported_options().c_str(), &interface, &error_count)) != EOF) {
371  process_command_line_option(c);
372  }
373  if (error_count > 0) {
374  return false;
375  }
376  return (error_count == 0);
377  }
378 
379  /**
380  * @brief Get a list of command line options supported by pmdaGetOpt.
381  *
382  * This function returns a getopt compatible list of command line options
383  * that are known to be supported by PCP's pmdaGetOpt function. Derived
384  * classes may choose to build upon this list to add new command line
385  * options in addition to those already built-in to PCP.
386  *
387  * @return A list of command line options supported by pmdaGetOpt.
388  *
389  * @see pmdaGetOpt
390  * @see getopt
391  */
392  static std::string pcp_builtin_options()
393  {
394  return "d:D:h:i:l:pu:6:";
395  }
396 
397  /**
398  * @brief Get a list of command line options supported by this PMDA.
399  *
400  * This base implementation simply returns the PCP built-in command line
401  * options, as returned by pcp_builtin_options.
402  *
403  * Derived classes may override this function to add to, or even replace,
404  * the set of command line options supported by the derived PMDA.
405  *
406  * @return A list of command line options supported by this PMDA.
407  */
408  virtual std::string get_supported_options() const
409  {
410  return pcp_builtin_options();
411  }
412 
413  /**
414  * @brief Process as custom command line option.
415  *
416  * This function will be invoked by parse_command_line function for any
417  * option not supported by pmdaGetOpt. That is, if a derived class
418  * overrides get_supported_options to include new custom command line
419  * options, then that derived class should also override this function to
420  * handle each of those command line options as they arise.
421  *
422  * This base implementation simply throws a generic pcp::exception.
423  *
424  * @param c The command line option character.
425  *
426  * @throw pcp::exception on error.
427  */
428  virtual void process_command_line_option(const int c)
429  {
430  throw pcp::exception(PM_ERR_GENERIC, (c == '?')
431  ? std::string("unrecognised option '-") + (char)optopt + "')"
432  : std::string("unimplemented option '-") + char(c) + "')");
433  }
434 #else
435  /**
436  * @brief Parse command line options.
437  *
438  * Override this overload if you want to customise the command line parsing
439  * process, but don't want to access any of the parsed options explicitly.
440  *
441  * Note, it's pretty unusual to want to do this. This overload is really
442  * here to maintain compatibility between the Boost and non-Boost
443  * implementations of parse_command_line.
444  *
445  * @param argc Argument count.
446  * @param argv Argumnet vector.
447  * @param interface PMDA interface.
448  *
449  * @return \c true if the caller should continue to run the PMDA, else
450  * \c false.
451  *
452  * @throw pcp::exception On error.
453  * @throw boost::program_options::error On parse error.
454  */
455  virtual bool parse_command_line(const int argc, const char * const argv[],
456  pmdaInterface& interface)
457  {
458  boost::program_options::variables_map options;
459  return parse_command_line(argc, argv, interface, options);
460  }
461 
462  /**
463  * @brief Parse command line options.
464  *
465  * Override this function to gain access explicit to the parsed command line
466  * options, if desired.
467  *
468  * @param argc Argument count.
469  * @param argv Argumnet vector.
470  * @param interface PMDA interface.
471  * @param options The parsed program options.
472  *
473  * @return \c true if the caller should continue to run the PMDA, else
474  * \c false.
475  *
476  * @throw pcp::exception On error.
477  * @throw boost::program_options::error On parse error.
478  */
479  virtual bool parse_command_line(const int argc, const char * const argv[],
480  pmdaInterface& interface,
481  boost::program_options::variables_map &options)
482  {
483  using namespace boost::program_options;
484  const options_description supported_options =
486  store(command_line_parser(argc, argv).options(supported_options)
487  .positional(get_supported_positional_options()).run(), options);
488 
489  const variable_value &config = options.at("config");
490  std::ifstream config_stream(config.as<std::string>().c_str());
491  if (config_stream) {
492  store(parse_config_file(config_stream, supported_options), options);
493  } else if (!config.defaulted()) {
494  throw pcp::exception(PM_ERR_GENERIC, "Failed to open config file: " +
495  config.as<std::string>());
496  }
497 
498 #ifdef PCP_CPP_DEBUG_COMMAND_LINE_OPTIONS
499  for (variables_map::const_iterator iter = options.begin(); iter != options.end(); ++iter) {
500  std::string value;
501  try {
502  if (iter->second.value().type() == typeid(int)) {
503  value = boost::lexical_cast<std::string>(iter->second.as<int>());
504  } else {
505  value = iter->second.as<std::string>();
506  }
507  } catch (const boost::bad_any_cast &ex) {
508  value = "(complex type \"" + std::string(iter->second.value().type().name()) + "\")";
509  }
510  std::cout << iter->first << ": " << value << std::endl;
511  }
512 #endif
513 
514  // Check if the --help (or -h) flag was given.
515  if (options.count("help") > 0) {
516  display_help(((argc > 0) && (argv[0] != '\0')) ? std::string(argv[0]) : get_pmda_name());
517  return false;
518  }
519 
520  // Check if the --version (or -v) flag was given.
521  if (options.count("version") > 0) {
522  display_version();
523  return false;
524  }
525 
526  // Check if any of the export flags were given.
527  bool exported = false;
528  #define PCP_CPP_EXPORT(type, func) \
529  if (options.count("export-" type) > 0) { \
530  if (supported_metrics.empty()) { \
531  supported_metrics = get_supported_metrics(); \
532  } \
533  const string_vector &filenames = options.at("export-" type).as<string_vector>(); \
534  for (string_vector::const_iterator iter = filenames.begin(); iter != filenames.end(); ++iter) { \
535  func(*iter); \
536  } \
537  exported = true; \
538  }
539  PCP_CPP_EXPORT("all", export_support_files);
540  PCP_CPP_EXPORT("domain", export_domain_header);
541  PCP_CPP_EXPORT("help", export_help_text);
542  PCP_CPP_EXPORT("pmns", export_pmns_data);
543  PCP_CPP_EXPORT("root", export_pmns_root);
544  #undef PCP_CPP_EXPORT
545  if (exported) {
546  return false;
547  }
548 
549  check_conflicting_options(options, "inet", "pipe");
550  check_conflicting_options(options, "inet", "unix");
551  check_conflicting_options(options, "inet", "inet6");
552  check_conflicting_options(options, "pipe", "unix");
553  check_conflicting_options(options, "pipe", "inet6");
554  check_conflicting_options(options, "unix", "inet6");
555 
556  // Apply all of the PCP built-in options the same as pmdaGetOpt would.
557  if (options.count("debug") > 0) {
558  const string_vector &values = options.at("debug").as<string_vector>();
559  for (string_vector::const_iterator iter = values.begin(); iter != values.end(); ++iter) {
560  const int result = __pmParseDebug(iter->c_str());
561  if (result < 0) {
562  throw pcp::exception(result,
563  "unrecognized debug flag specification '" + *iter + "'");
564  }
565  pmDebug |= result;
566  }
567  }
568  if (options.count("domain") > 0) {
569  interface.domain = options.at("domain").as<int>();
570  }
571  if (options.count("help-file") > 0) {
572  char * const help_file = strdup(options.at("help-file").as<std::string>().c_str());
573  interface.version.two.ext->e_helptext = help_file;
574  free_on_destruction.push(help_file);
575  }
576  if (options.count("inet") > 0) {
577  interface.version.two.ext->e_io = pmdaInet;
578  interface.version.two.ext->e_port = options.at("inet").as<int>();
579  }
580  if (options.count("log-file") > 0) {
581  char * const log_file = strdup(options.at("log-file").as<std::string>().c_str());
582  interface.version.two.ext->e_logfile = log_file;
583  free_on_destruction.push(log_file);
584  }
585  if (options.count("pipe") > 0) {
586  interface.version.two.ext->e_io = pmdaPipe;
587  }
588  if (options.count("unix") > 0) {
589  interface.version.two.ext->e_io = pmdaUnix;
590  free_on_destruction.push(interface.version.two.ext->e_sockname =
591  strdup(options.at("unix").as<std::string>().c_str()));
592  }
593  if (options.count("inet6") > 0) {
594  // The pmdaIPv6 value was added to PCP in version 3.8.1. There is
595  // no reliable way to detect PCP's version at compile time, so we
596  // use it's known value (4) here to prevent issues compiling against
597  // earlier versions of PCP. Note, the default get_supported_options
598  // implementation below does not include inet6 in the command line
599  // options for earlier versions of PCP, as detected at runtime.
600  interface.version.two.ext->e_io = static_cast<pmdaIoType>(4);
601  interface.version.two.ext->e_port = options.at("inet6").as<int>();
602  }
603  return true;
604  }
605 
606  /**
607  * @brief Get a list of command line options built into the PCP libraries.
608  *
609  * @return A list of command line options built into the PCP libraries.
610  */
611  virtual boost::program_options::options_description pcp_builtin_options() const
612  {
613  using namespace boost::program_options;
614  options_description options("Libpcp-pmda options");
615  options.add_options()
616  ("debug,D", value<string_vector>()
618  PCP_CPP_BOOST_PO_VALUE_NAME("spec"), "set debug specification")
619  ("domain,d", value<int>()->default_value(get_default_pmda_domain_number())
620  PCP_CPP_BOOST_PO_VALUE_NAME("n"), "domain number to use")
621  ("help-file,h",
622  value<std::string>()->default_value(get_help_text_pathname())
623  PCP_CPP_BOOST_PO_VALUE_NAME("file"), "file containing help text")
624  ("inet,i", value<int>() PCP_CPP_BOOST_PO_VALUE_NAME("port"),
625  "use inet port for pmcd comms; conflicts with -p, -u and -6")
626  ("log-file,l", value<std::string>()->default_value(get_log_file_pathname())
627  PCP_CPP_BOOST_PO_VALUE_NAME("file"), "file to use for logging")
628  ("pipe,p", "use stdin/stdout for pmcd comms; conflicts with -i, -u and -6")
629  ("unix,u", value<std::string>() PCP_CPP_BOOST_PO_VALUE_NAME("socket"),
630  "use named socket for pmcd comms; conflicts with -i, -p and -6");
631  if (get_pcp_runtime_version<uint_fast32_t>() >= 0x30801) {
632  options.add_options()
633  ("inet6,6", value<int>() PCP_CPP_BOOST_PO_VALUE_NAME("port"),
634  "use IPv6 port for pmcd comms; conflicts with -i, -p and -u");
635  }
636  return options;
637  }
638 
639  /**
640  * @brief Get a list of command line options supported by this PMDA.
641  *
642  * This base implementation returns a set of options including options
643  * supported by the PCP libraries, as well as a number of custom options
644  * implemented by this pcp::pmda class (such as --help and --version).
645  *
646  * Derived classes may override this function to add to, or even replace,
647  * the set of options supported by the derived PMDA.
648  *
649  * @return A list of command line options supported by this PMDA.
650  */
651  virtual boost::program_options::options_description get_supported_options() const
652  {
653  using namespace boost::program_options;
654  options_description options("Extra options");
655  options.add_options()
656  ("config", value<std::string>()->default_value(get_config_file_pathname())
657  PCP_CPP_BOOST_PO_VALUE_NAME("file"), "load options from config file")
658  ("export-all", value<string_vector>()
660  PCP_CPP_BOOST_PO_VALUE_NAME("dir"), "export domain, help, pmns and root then exit")
661  ("export-domain", value<string_vector>()
663  PCP_CPP_BOOST_PO_VALUE_NAME("file"), "export domain header then exit")
664  ("export-help", value<string_vector>()
666  PCP_CPP_BOOST_PO_VALUE_NAME("file"), "export help text then exit")
667  ("export-pmns", value<string_vector>()
669  PCP_CPP_BOOST_PO_VALUE_NAME("file"), "export pmns text then exit")
670  ("export-root", value<string_vector>()
672  PCP_CPP_BOOST_PO_VALUE_NAME("file"), "export pmns root then exit")
673  ("help", "display this message then exit")
674  ("version", "display version info then exit");
675  return pcp_builtin_options().add(options);
676  }
677 
678  /**
679  * @brief Get a list of hidden supported command line options.
680  *
681  * Hidden command line options behave just like their non-hidden veriety,
682  * except that they are not included in the output of the --help option.
683  *
684  * This is allows derived classes to implement, for example, development
685  * only options or other options that would be confusing to end users,
686  * without necessary having to make the obvious.
687  *
688  * This, of course, should be used sparingly.
689  *
690  * This base implementation returns an empty list.
691  *
692  * @return A list of command line options to support, but not advertise.
693  */
694  virtual boost::program_options::options_description get_supported_hidden_options() const
695  {
696  return boost::program_options::options_description();
697  }
698 
699  /**
700  * @brief Get a list of supported positional options.
701  *
702  * Derived classes may override this function to support positional options.
703  *
704  * This base implementation returns an empty list.
705  *
706  * @return A list of supported positional options.
707  */
708  virtual boost::program_options::positional_options_description get_supported_positional_options()
709  {
710  return boost::program_options::positional_options_description();
711  }
712 
713  /**
714  * @brief Check for conflicting command line options.
715  *
716  * This convenience function can be used to check, in a standardised way,
717  * that two mutually exclusive options are not both set.
718  *
719  * @param options_map Parsed command line options.
720  * @param option1 The first of two options that are mutually exclusive.
721  * @param option2 The second of two options that are mutually exclusive.
722  *
723  * @throw boost::program_options::error If both \a option1 and \a option2
724  * are set, and neither were defaulted.
725  */
726  static void check_conflicting_options(const boost::program_options::variables_map &options_map,
727  const std::string &option1, const std::string &option2)
728  {
729  // If both options are specified, and neither was defaulted...
730  if ((options_map.count(option1)>0) && (!options_map[option1].defaulted()) &&
731  (options_map.count(option2)>0) && (!options_map[option2].defaulted())) {
732  throw boost::program_options::error(
733  "conflicting options '" + option1 + "' and '" + option2 + "'.");
734  }
735  }
736 
737  /**
738  * @brief Display a help message.
739  *
740  * This function is called if the --help command line option was given.
741  *
742  * Derived classes may override this function to customise the help text
743  * output.
744  *
745  * @param program_name The program name to include in the help message.
746  */
747  virtual void display_help(const std::string &program_name) const
748  {
749  std::cout
750  << std::endl << "Usage: " << get_usage(program_name) << std::endl
751  << std::endl << get_supported_options() << std::endl;
752  }
753 
754 #endif
755 
756  /**
757  * @brief Display a version message.
758  *
759  * This function is called if the --version command line option was given.
760  *
761  * Derived classes may override this function to customise the version text
762  * output.
763  *
764  * @see get_pmda_version
765  */
766  virtual void display_version() const
767  {
768  const std::ostream::fmtflags cout_format_flags(std::cout.flags());
769 
770  std::cout << std::endl << get_pmda_name() << " PMDA";
771  const std::string pmda_version = get_pmda_version();
772  if (!pmda_version.empty()) {
773  std::cout << " version " << pmda_version;
774  }
775  std::cout << std::endl << "PMDA interface version "
777  << std::endl << "PCP version "
778  << get_pcp_runtime_version<char *>() << " ("
779  << std::hex << get_pcp_runtime_version<uint_fast32_t>()
780  << ')' << std::endl << std::endl;
781 
782  std::cout.flags(cout_format_flags); // Restore cout to its entry state.
783  }
784 
785  /**
786  * @brief Get a usage syntax string.
787  *
788  * This function returns a string defining the command line syntax. It is
789  * included, by default, in the text output of the --help command line
790  * option.
791  *
792  * This base implementation returns a string like: `program [options]`. But,
793  * for example, if a derived class made some options compulsory, and/or
794  * allowed position arguments, then that class would want to override this
795  * function to return something like:
796  * `program --required-flag=arg [options] filename1 filename2`.
797  *
798  * @param program_name The name of the program to include in the syntax line.
799  *
800  * @return A usage syntax line.
801  *
802  * @see display_help
803  */
804  virtual std::string get_usage(const std::string &program_name) const
805  {
806  return program_name + " [options]";
807  }
808 
809  /**
810  * @brief Initialise a DSO interface with this PMDA.
811  *
812  * @param interface PMDA interface to initialise.
813  *
814  * @throws pcp::exception on error.
815  */
816  virtual void initialize_dso(pmdaInterface &interface) {
817  const std::string help_text_pathname = get_help_text_pathname();
818 
819  // Contrary to the man pages, pmdaDSO returns void, not int.
820  pmdaDSO(&interface, PCP_CPP_PMDA_INTERFACE_VERSION,
821  const_cast<char *>(get_pmda_name().c_str()),
822  (help_text_pathname.empty()) ? NULL : const_cast<char *>(help_text_pathname.c_str()));
823 
824  // Handle pmdaDSO errors (the man page is really lacking here).
825  if (interface.comm.pmda_interface < PCP_CPP_PMDA_INTERFACE_VERSION) {
826  std::ostringstream message;
827  message << "This DSO uses protocol " << PCP_CPP_PMDA_INTERFACE_VERSION
828  << " but the caller only understands " << interface.comm.pmda_interface
829  << " or less";
830  throw pcp::exception(PM_ERR_GENERIC, message.str());
831  }
832  if (interface.status < 0) {
833  throw pcp::exception(interface.status, "Failed to initialize DSO via pmdaDSO");
834  }
835 
836  // Initialise the rest of the PMDA.
837  initialize_pmda(interface);
838  }
839 
840  /**
841  * @brief Initialise this PMDA.
842  *
843  * @param interface PMDA interface to initialise.
844  */
845  virtual void initialize_pmda(pmdaInterface &interface)
846  {
847  // Setup the instance domain and metrics tables. These will be
848  // assigned to members of the interface struct (by pmdaInit), so they
849  // must remain valid as long as the interface does.
850  supported_metrics = get_supported_metrics();
851  const std::pair<size_t,size_t> counts = count_metrics(supported_metrics);
852  const size_t indom_count = counts.second;
853  const size_t metric_count = counts.first;
854  pmdaIndom * indom_table = (indom_count == 0) ? NULL : new pmdaIndom [indom_count];
855  pmdaMetric * metric_table = new pmdaMetric [metric_count];
856 
857  std::map<const instance_domain *, pmInDom> instance_domain_ids;
858  std::vector<instance_domain *> instance_domains;
859  instance_domains.reserve(indom_count);
860  size_t metric_index = 0;
861  for (metrics_description::const_iterator metrics_iter = supported_metrics.begin();
862  metrics_iter != supported_metrics.end(); ++metrics_iter)
863  {
864  const metric_cluster &cluster = metrics_iter->second;
865  for (metric_cluster::const_iterator cluster_iter = cluster.begin();
866  cluster_iter != cluster.end(); ++cluster_iter)
867  {
868  const metric_description &description = cluster_iter->second;
869  assert(metric_index < metric_count);
870  metric_table[metric_index].m_desc = description;
871  metric_table[metric_index].m_desc.pmid =
872  PMDA_PMID(cluster.get_cluster_id(), cluster_iter->first);
873  if (description.domain != NULL) {
874  const std::pair<std::map<const instance_domain *, pmInDom>::const_iterator, bool>
875  insert_result = instance_domain_ids.insert(
876  std::make_pair(cluster_iter->second.domain, instance_domain_ids.size()));
877  const pmInDom indom = insert_result.first->second;
878  assert(indom < indom_count);
879  if (insert_result.second) {
880  indom_table[indom] = allocate_pmda_indom(*cluster_iter->second.domain);
881  instance_domains.push_back(cluster_iter->second.domain);
882  assert(instance_domain_ids.size() == instance_domains.size());
883  }
884  metric_table[metric_index].m_desc.indom = indom;
885  } else {
886  metric_table[metric_index].m_desc.indom = PM_INDOM_NULL;
887  }
888  metric_table[metric_index].m_user = description.opaque;
889  metric_index++;
890  }
891  }
892  assert(instance_domain_ids.size() == indom_count);
893  assert(instance_domains.size() == indom_count);
894  assert(metric_index == metric_count);
895 
896  // Assign our callback function pointers to the interface struct.
897  set_callbacks(interface);
898 
899  // Initialize the PMDA interface.
900  pmdaInit(&interface, indom_table, indom_count, metric_table, metric_count);
901 
902  // Record the pmdaIndom values as updated by pmdaInit.
903  for (size_t indom_index = 0; indom_index < indom_count; ++indom_index) {
904  instance_domain * const domain = instance_domains.at(indom_index);
905  domain->set_pm_instance_domain(indom_table[indom_index].it_indom);
906  this->instance_domains.insert(std::make_pair(domain->get_domain_id(), domain));
907  this->instance_domains.insert(std::make_pair(domain->get_pm_instance_domain(), domain));
908  }
909  }
910 
911  /**
912  * @brief Get descriptions of all of the metrics supported by this PMDA.
913  *
914  * Dervied classes must implement this function to define the metrics
915  * supported by this PMDA.
916  *
917  * @return Descriptions of all of the metrics supported by this PMDA.
918  */
920 
921  /**
922  * @brief Begin fetching values.
923  *
924  * Derived classes may override this function to perform any actions they
925  * wish to perform at the start of each batch of fetch of metric values.
926  *
927  * This base implementation performs no actions.
928  */
929  virtual void begin_fetch_values() { }
930 
931  /**
932  * @brief Fetch an individual metric value.
933  *
934  * Derived classes must implment this function to fetch individual metric
935  * values.
936  *
937  * If the requested metric value is not found, or some other error occurs,
938  * implementations should throw an appropriate pcp::exception, such as
939  * `pcp::exception(PMDA_FETCH_NOVALUES)`.
940  *
941  * Otherwise, the a valid atom value should be returned, encapsulated in a
942  * fetch_value_result struct. Typically the \c code value of the returned
943  * struct should be left as `PMDA_FETCH_STATIC`. However, advanced PMDAs may
944  * use any of the `PMDA_FETCH_*` constants, such as `PMDA_FETCH_DYNAMIC`.
945  *
946  * Note, implementations should not return `PMDA_FETCH_NOVALUES` - in that
947  * case, they should throw `pcp::exception(PMDA_FETCH_NOVALUES)` instead.
948  *
949  * @param metric The metric to fetch the value of.
950  *
951  * @throw pcp::exception on error, or if the requested metric is not
952  * currently available.
953  *
954  * @return The value of the requested metric.
955  */
956  virtual fetch_value_result fetch_value(const metric_id &metric) = 0;
957 
958  /**
959  * @brief Store an in situ value.
960  *
961  * Derived classes may override this function to allow PCP to request metric
962  * values to be stored.
963  *
964  * This base implementation throws `pcp::exception(PM_ERR_PERMISSION)`.
965  *
966  * @note Unless `PCP_CPP_NO_ID_VALIDITY_CHECKS` has been defined, this
967  * function will not be called for any metric that did not include
968  * the `storable_metric` flag in the get_supported_metrics result.
969  *
970  * @param metric The metric to store.
971  * @param value The value to store.
972  *
973  * @throw pcp::exception on error.
974  */
975  virtual void store_value(const metric_id &metric, const int &value)
976  {
977  PCP_CPP_UNUSED(metric)
978  PCP_CPP_UNUSED(value)
979  throw pcp::exception(PM_ERR_PERMISSION);
980  }
981 
982  /**
983  * @brief Store an ex situ value.
984  *
985  * Derived classes may override this function to allow PCP to request metric
986  * values to be stored.
987  *
988  * This base implementation throws `pcp::exception(PM_ERR_PERMISSION)`.
989  *
990  * @note Unless `PCP_CPP_NO_ID_VALIDITY_CHECKS` has been defined, this
991  * function will not be called for any metric that did not include
992  * the `storable_metric` flag in the get_supported_metrics result.
993  *
994  * @param metric The metric to store.
995  * @param value The value to store.
996  *
997  * @throw pcp::exception on error.
998  */
999  virtual void store_value(const metric_id &metric,
1000  const pmValueBlock * const value)
1001  {
1002  PCP_CPP_UNUSED(metric)
1003  PCP_CPP_UNUSED(value)
1004  throw pcp::exception(PM_ERR_PERMISSION);
1005  }
1006 
1007  /* Virtual PMDA callback functions below here. You probably don't
1008  * want to override any of these, but you can if you want to. */
1009 
1010 #if PCP_CPP_PMDA_INTERFACE_VERSION >= 6
1011  /// Inform the agent about security aspects of a client connection,
1012  /// such as the authenticated userid. Passed in a client identifier,
1013  /// numeric PCP_ATTR, pointer to the associated value, and the length
1014  /// of the value.
1015  virtual int on_attribute(int ctx, int attr, const char *value,
1016  int length, pmdaExt *pmda)
1017  {
1018  return pmdaAttribute(ctx, attr, value, length, pmda);
1019  }
1020 #endif
1021 
1022 #if PCP_CPP_PMDA_INTERFACE_VERSION >= 4
1023  /// If traverse == 0, return the names of all the descendent children
1024  /// and their status, given a named metric in a dynamic subtree of
1025  /// the PMNS (this is the pmGetChildren or pmGetChildrenStatus variant).
1026  /// If traverse == 1, return the full names of all descendent metrics
1027  /// (this is the pmTraversePMNS variant, with the status added)
1028  virtual int on_children(const char *name, int traverse, char ***kids,
1029  int **sts, pmdaExt *pmda)
1030  {
1031  return pmdaChildren(name, traverse, kids, sts, pmda);
1032  }
1033 #endif
1034 
1035  /// @brief Return the metric desciption.
1036  virtual int on_desc(pmID pmid, pmDesc *desc, pmdaExt *pmda)
1037  {
1038  return pmdaDesc(pmid, desc, pmda);
1039  }
1040 
1041  /// @brief Resize the pmResult and call e_callback in the pmdaExt structure
1042  /// for each metric instance required by the profile.
1043  virtual int on_fetch(int numpmid, pmID *pmidlist, pmResult **resp,
1044  pmdaExt *pmda)
1045  {
1046  try {
1048  } catch (const pcp::exception &ex) {
1049  __pmNotifyErr(LOG_ERR, "%s", ex.what());
1050  return ex.error_code();
1051  }
1052  return pmdaFetch(numpmid, pmidlist, resp, pmda);
1053  }
1054 
1055  /// @brief Fetch the value of a single metric instance.
1056  virtual int on_fetch_callback(pmdaMetric *mdesc, unsigned int inst,
1057  pmAtomValue *avp)
1058  {
1059  try {
1060  // Setup the metric ID.
1061  metric_id id;
1062  id.cluster = pmid_cluster(mdesc->m_desc.pmid);
1063  id.instance = inst;
1064  id.item = pmid_item(mdesc->m_desc.pmid);
1065  id.opaque = mdesc->m_user;
1066 
1067 #ifdef PCP_CPP_NO_ID_VALIDITY_CHECKS
1068  id.type = PM_TYPE_UNKNOWN;
1069 #else
1070  const metric_description &description =
1071  supported_metrics.at(id.cluster).at(id.item);
1072  id.type = description.type;
1073  validate_instance(description, inst);
1074 #endif
1075 
1076  // Fetch the metric value.
1077  const fetch_value_result result = fetch_value(id);
1078  *avp = result.atom;
1079 #if PCP_CPP_PMDA_INTERFACE_VERSION <= 2
1080  return 0; // "No error" for PMDA interface 2.
1081 #elif PCP_CPP_PMDA_INTERFACE_VERSION < 4
1082  return 1; // "Metric found" for PMDA interfaces 3 and 4.
1083 #else // PCP_CPP_PMDA_INTERFACE_VERSION >= 5
1084  return result.code; // PMDA_FETCH_* values for inerfaces 5+
1085 #endif
1086  } catch (const pcp::exception &ex) {
1087  if (ex.error_code() != PMDA_FETCH_NOVALUES) {
1088  __pmNotifyErr(LOG_ERR, "%s", ex.what());
1089  }
1090  return ex.error_code();
1091  } catch (const std::out_of_range &ex) {
1092  __pmNotifyErr(LOG_DEBUG, "%s:%d:%s %s", __FILE__, __LINE__, __FUNCTION__, ex.what());
1093  return PM_ERR_PMID; // Unknown or illegal metric identifier.
1094  } catch (const std::exception &ex) {
1095  __pmNotifyErr(LOG_ERR, "%s", ex.what());
1096  return PM_ERR_GENERIC;
1097  } catch (...) {
1098  __pmNotifyErr(LOG_ERR, "unknown exception in on_fetch_callback");
1099  return PM_ERR_GENERIC;
1100  }
1101  }
1102 
1103  /// @brief Return description of instances and instance domains.
1104  virtual int on_instance(pmInDom indom, int inst, char *name,
1105  __pmInResult **result, pmdaExt *pmda)
1106  {
1107  return pmdaInstance(indom, inst, name, result, pmda);
1108  }
1109 
1110 #if PCP_CPP_PMDA_INTERFACE_VERSION >= 4
1111  /// @brief Given a PMID, return the names of all matching metrics within a
1112  /// dynamic subtree of the PMNS.
1113  virtual int on_name(pmID pmid, char ***nameset, pmdaExt *pmda)
1114  {
1115  return pmdaName(pmid, nameset, pmda);
1116  }
1117 
1118  /// @brief Return the PMID for a named metric within a dynamic subtree
1119  /// of the PMNS.
1120  virtual int on_pmid(const char *name, pmID *pmid, pmdaExt *pmda)
1121  {
1122  return pmdaPMID(name, pmid, pmda);
1123  }
1124 #endif
1125 
1126  /// @brief Store the instance profile away for the next fetch.
1127  virtual int on_profile(__pmProfile *prof, pmdaExt *pmda)
1128  {
1129  return pmdaProfile(prof, pmda);
1130  }
1131 
1132  /// @brief Store a value into a metric.
1133  virtual int on_store(pmResult *result, pmdaExt *pmda)
1134  {
1135  __pmNotifyErr(LOG_INFO, "on store");
1136  try {
1137  bool any_stored = false;
1138  for (int value_set_index = 0; value_set_index < result->numpmid; ++value_set_index) {
1139  pmValueSet * const value_set = result->vset[value_set_index];
1140 
1141  // Setup the metric ID.
1142  metric_id id;
1143  id.cluster = pmid_cluster(value_set->pmid);
1144  id.item = pmid_item(value_set->pmid);
1145  id.opaque = NULL;
1146  id.type = PM_TYPE_UNKNOWN;
1147 
1148  for (int instance_index = 0; instance_index < value_set->numval; ++instance_index) {
1149  id.instance = value_set->vlist[instance_index].inst;
1150 #ifndef PCP_CPP_NO_ID_VALIDITY_CHECKS
1151  const metric_description &description =
1152  supported_metrics.at(id.cluster).at(id.item);
1153  id.type = description.type;
1154 
1155  validate_instance(description, id.instance);
1156 
1157  if (!(description.flags & pcp::storable_metric)) {
1158  // Metric does not support storing values.
1159  throw pcp::exception(PM_ERR_PERMISSION);
1160  }
1161 #endif
1162  if (value_set->valfmt == PM_VAL_INSITU) {
1163  store_value(id, value_set->vlist[instance_index].value.lval);
1164  } else {
1165  store_value(id, value_set->vlist[instance_index].value.pval);
1166  }
1167  any_stored = true;
1168  }
1169  }
1170  if (any_stored) {
1171  return 0; // >= 0 implies success
1172  }
1173  } catch (const pcp::exception &ex) {
1174  if (ex.error_code() != PMDA_FETCH_NOVALUES) {
1175  __pmNotifyErr(LOG_ERR, "%s", ex.what());
1176  }
1177  return ex.error_code();
1178  } catch (const std::out_of_range &ex) {
1179  __pmNotifyErr(LOG_DEBUG, "%s:%d:%s %s", __FILE__, __LINE__, __FUNCTION__, ex.what());
1180  return PM_ERR_PMID; // Unknown or illegal metric identifier.
1181  } catch (const std::exception &ex) {
1182  __pmNotifyErr(LOG_ERR, "%s", ex.what());
1183  return PM_ERR_GENERIC;
1184  } catch (...) {
1185  __pmNotifyErr(LOG_ERR, "unknown exception in on_fetch_callback");
1186  return PM_ERR_GENERIC;
1187  }
1188  return pmdaStore(result, pmda); // Just returns PM_ERR_PERMISSION.
1189  }
1190 
1191  /// @brief Return the help text for the metric.
1192  virtual int on_text(int ident, int type, char **buffer, pmdaExt *pmda)
1193  {
1194  try {
1195  const bool get_one_line = ((type & PM_TEXT_ONELINE) == PM_TEXT_ONELINE);
1196  if ((type & PM_TEXT_PMID) == PM_TEXT_PMID) {
1197  const metric_description &description =
1198  supported_metrics.at(pmid_cluster(ident)).at(pmid_item(ident));
1199  const std::string &text = get_one_line
1200  ? description.short_description.empty()
1201  ? description.verbose_description
1202  : description.short_description
1203  : description.verbose_description.empty()
1204  ? description.short_description
1205  : description.verbose_description;
1206  if (text.empty()) {
1207  throw pcp::exception(PM_ERR_TEXT);
1208  }
1209  *buffer = strdup(text.c_str());
1210  return 0; // >= 0 implies success.
1211  } else if ((type & PM_TEXT_INDOM) == PM_TEXT_INDOM) {
1212  const pcp::instance_info &info =
1213  instance_domains.at(pmInDom_domain(ident))->at(pmInDom_serial(ident));
1214  const std::string &text = get_one_line
1215  ? info.short_description.empty()
1216  ? info.verbose_description
1217  : info.short_description
1218  : info.verbose_description.empty()
1219  ? info.short_description
1220  : info.verbose_description;
1221  if (text.empty()) {
1222  throw pcp::exception(PM_ERR_TEXT);
1223  }
1224  *buffer = strdup(text.c_str());
1225  return 0; // >= 0 implies success.
1226  } else {
1227  __pmNotifyErr(LOG_NOTICE, "unknown text type 0x%x", type);
1228  }
1229  } catch (const pcp::exception &ex) {
1230  if (ex.error_code() != PM_ERR_TEXT) {
1231  __pmNotifyErr(LOG_NOTICE, "%s", ex.what());
1232  } else {
1233  __pmNotifyErr(LOG_DEBUG, "%s:%d:%s %s", __FILE__, __LINE__, __FUNCTION__, ex.what());
1234  }
1235  } catch (const std::out_of_range &ex) {
1236  __pmNotifyErr(LOG_DEBUG, "%s:%d:%s %s", __FILE__, __LINE__, __FUNCTION__, ex.what());
1237  } catch (const std::exception &ex) {
1238  __pmNotifyErr(LOG_NOTICE, "%s", ex.what());
1239  } catch (...) {
1240  __pmNotifyErr(LOG_ERR, "unknown exception in on_text");
1241  return PM_ERR_GENERIC;
1242  }
1243  return pmdaText(ident, type, buffer, pmda);
1244  }
1245 
1246  /**
1247  * @brief Set static callbacks on a PMDA interface.
1248  *
1249  * This function sets our static PMDA callback functions on the given
1250  * pmdaInterface struct. These static callback functions then redirect all
1251  * calls to the current singleton PMDA instance.
1252  *
1253  * @param interface The interface to set our callbacks on.
1254  */
1255  virtual void set_callbacks(pmdaInterface &interface)
1256  {
1257  interface.version.two.profile = &callback_profile;
1258  interface.version.two.fetch = &callback_fetch;
1259  interface.version.two.desc = &callback_desc;
1260  interface.version.two.instance = &callback_instance;
1261  interface.version.two.text = &callback_text;
1262  interface.version.two.store = &callback_store;
1263  #if PCP_CPP_PMDA_INTERFACE_VERSION >= 4
1264  interface.version.four.pmid = &callback_pmid;
1265  interface.version.four.name = &callback_name;
1266  interface.version.four.children = &callback_children;
1267  #endif
1268  #if PCP_CPP_PMDA_INTERFACE_VERSION >= 6
1269  interface.version.six.attribute = &callback_attribute;
1270  #endif
1271 
1272  //pmdaSetResultCallBack(&interface, ...);
1273  pmdaSetFetchCallBack(&interface, &callback_fetch_callback);
1274  //pmdaSetCheckCallBack(&interface, ...);
1275  //pmdaSetDoneCallBack(&interface, ...);
1276  //pmdaSetEndContextCallBack(&interface, ...);
1277  }
1278 
1279 private:
1280  static pmda * instance;
1281  std::stack<void *> free_on_destruction;
1282  std::map<pmInDom, instance_domain *> instance_domains;
1283 
1284  void export_domain_header(const std::string &filename) const
1285  {
1286  // Open the output file.
1287  std::ofstream file_stream;
1288  if (filename != "-") {
1289  file_stream.open(filename.c_str());
1290  if (!file_stream.is_open()) {
1291  throw pcp::exception(PM_ERR_GENERIC, "failed to open file for writing: " + filename);
1292  }
1293  }
1294  std::ostream &stream = (filename == "-") ? std::cout : file_stream;
1295 
1296  // Export the header file content.
1297  std::string upper_name = get_pmda_name();
1298  std::transform(upper_name.begin(), upper_name.end(), upper_name.begin(), ::toupper);
1299  stream
1300  << "/* The " << get_pmda_name() << " PMDA's domain number. */" << std::endl
1301  << "#define " << upper_name << ' ' << get_default_pmda_domain_number() << std::endl;
1302  }
1303 
1304  void export_help_text(const std::string &filename) const
1305  {
1306  // Generated ordered maps of metric names and instance IDs to help texts.
1307  std::map<std::string, metric_description> metrics;
1308  std::map<domain_id_type, const instance_domain *> instances;
1309  const std::string pmda_name = get_pmda_name();
1310  for (metrics_description::const_iterator metrics_iter = supported_metrics.begin();
1311  metrics_iter != supported_metrics.end(); ++metrics_iter)
1312  {
1313  const metric_cluster &cluster = metrics_iter->second;
1314  const std::string cluster_name = cluster.get_cluster_name();
1315  for (metric_cluster::const_iterator cluster_iter = cluster.begin();
1316  cluster_iter != cluster.end(); ++cluster_iter)
1317  {
1318  const metric_description &metric = cluster_iter->second;
1319  std::string metric_name = pmda_name;
1320  if (!cluster_name.empty()) {
1321  metric_name += '.' + cluster_name;
1322  }
1323  metric_name += '.' + metric.metric_name;
1324  metrics.insert(std::make_pair(metric_name, metric));
1325  if (metric.domain != NULL) {
1326  instances.insert(std::make_pair(metric.domain->get_domain_id(), metric.domain));
1327  }
1328  }
1329  }
1330 
1331  // Open the output file.
1332  std::ofstream file_stream;
1333  if (filename != "-") {
1334  file_stream.open(filename.c_str());
1335  if (!file_stream.is_open()) {
1336  throw pcp::exception(PM_ERR_GENERIC, "failed to open file for writing: " + filename);
1337  }
1338  }
1339  std::ostream &stream = (filename == "-") ? std::cout : file_stream;
1340 
1341  // Export the help text.
1342  stream << std::endl;
1343  for (std::map<std::string, metric_description>::const_iterator metric = metrics.begin();
1344  metric != metrics.end(); ++metric)
1345  {
1346  stream << "@ " << metric->first << ' ' << metric->second.short_description << std::endl;
1347  if (!metric->second.verbose_description.empty()) {
1348  stream << metric->second.verbose_description << std::endl;
1349  }
1350  stream << std::endl;
1351  }
1352  for (std::map<domain_id_type, const instance_domain *>::const_iterator indom = instances.begin();
1353  indom != instances.end(); ++indom)
1354  {
1355  for (instance_domain::const_iterator instance = indom->second->begin();
1356  instance != indom->second->end(); ++instance)
1357  {
1358  stream << "@ " << indom->first << '.' << instance->first
1359  << ' ' << instance->second.short_description << std::endl;
1360  if (!instance->second.verbose_description.empty()) {
1361  stream << instance->second.verbose_description << std::endl;
1362  }
1363  stream << std::endl;
1364  }
1365  }
1366  }
1367 
1368  void export_pmns_data(const std::string &filename) const
1369  {
1370  // Some basic strings we'll use a couple of times.
1371  const std::string &pmda_name = get_pmda_name();
1372  std::string upper_name = get_pmda_name();
1373  std::transform(upper_name.begin(), upper_name.end(), upper_name.begin(), ::toupper);
1374 
1375  // Open the output file.
1376  std::ofstream file_stream;
1377  if (filename != "-") {
1378  file_stream.open(filename.c_str());
1379  if (!file_stream.is_open()) {
1380  throw pcp::exception(PM_ERR_GENERIC, "failed to open file for writing: " + filename);
1381  }
1382  }
1383  std::ostream &stream = (filename == "-") ? std::cout : file_stream;
1384 
1385  // Define the PMID, if not already.
1386  stream
1387  << std::endl
1388  << "#ifndef " << upper_name << std::endl
1389  << "#define " << upper_name << ' ' << get_default_pmda_domain_number() << std::endl
1390  << "#endif" << std::endl;
1391 
1392  // First pass to find the length of the longest metric name.
1393  std::string::size_type max_metric_name_size = 0;
1394  for (metrics_description::const_iterator metrics_iter = supported_metrics.begin();
1395  metrics_iter != supported_metrics.end(); ++metrics_iter)
1396  {
1397  const metric_cluster &cluster = metrics_iter->second;
1398  for (metric_cluster::const_iterator cluster_iter = cluster.begin();
1399  cluster_iter != cluster.end(); ++cluster_iter)
1400  {
1401  if (cluster_iter->second.metric_name.size() > max_metric_name_size) {
1402  max_metric_name_size = cluster_iter->second.metric_name.size();
1403  }
1404  }
1405  }
1406 
1407  // Second pass to export the group names and ungrouped metrics.
1408  stream << std::endl << pmda_name << " {" << std::endl;
1409  for (metrics_description::const_iterator metrics_iter = supported_metrics.begin();
1410  metrics_iter != supported_metrics.end(); ++metrics_iter)
1411  {
1412  const metric_cluster &cluster = metrics_iter->second;
1413  const std::string cluster_name = cluster.get_cluster_name();
1414  if (cluster_name.empty()) {
1415  for (metric_cluster::const_iterator cluster_iter = cluster.begin();
1416  cluster_iter != cluster.end(); ++cluster_iter)
1417  {
1418  stream << " " << cluster_iter->second.metric_name
1419  << std::string(max_metric_name_size - cluster_iter->second.metric_name.size() + 4, ' ')
1420  << upper_name << ':' << cluster.get_cluster_id() << ':'
1421  << cluster_iter->first << std::endl;
1422  }
1423  } else {
1424  static std::string previous_cluster_name;
1425  if (cluster_name != previous_cluster_name) {
1426  stream << " " << cluster_name << std::endl;
1427  previous_cluster_name = cluster_name;
1428  }
1429  }
1430  }
1431  stream << '}' << std::endl;
1432 
1433  // Third and final pass to export all metric groups.
1434  std::string previous_cluster_name;
1435  for (metrics_description::const_iterator metrics_iter = supported_metrics.begin();
1436  metrics_iter != supported_metrics.end(); ++metrics_iter)
1437  {
1438  const metric_cluster &cluster = metrics_iter->second;
1439  const std::string cluster_name = cluster.get_cluster_name();
1440  if (!cluster_name.empty()) {
1441  if (cluster_name != previous_cluster_name) {
1442  if (!previous_cluster_name.empty()) {
1443  stream << "}" << std::endl;
1444  }
1445  stream << std::endl << pmda_name << '.' << cluster_name << " {" << std::endl;
1446  }
1447  for (metric_cluster::const_iterator cluster_iter = cluster.begin();
1448  cluster_iter != cluster.end(); ++cluster_iter)
1449  {
1450  stream << " " << cluster_iter->second.metric_name
1451  << std::string(max_metric_name_size - cluster_iter->second.metric_name.size() + 4, ' ')
1452  << upper_name << ':' << cluster.get_cluster_id() << ':'
1453  << cluster_iter->first << std::endl;
1454  }
1455  previous_cluster_name = cluster_name;
1456  }
1457  }
1458  if (!previous_cluster_name.empty()) {
1459  stream << "}" << std::endl;
1460  }
1461  stream << std::endl;
1462  }
1463 
1464  void export_pmns_root(const std::string &filename) const
1465  {
1466  // Open the output file.
1467  std::ofstream file_stream;
1468  if (filename != "-") {
1469  file_stream.open(filename.c_str());
1470  if (!file_stream.is_open()) {
1471  throw pcp::exception(PM_ERR_GENERIC, "failed to open file for writing: " + filename);
1472  }
1473  }
1474  std::ostream &stream = (filename == "-") ? std::cout : file_stream;
1475  stream
1476  << std::endl
1477  << "root { " << get_pmda_name() << " }" << std::endl << std::endl
1478  << "#include \"pmns\"" << std::endl << std::endl;
1479  }
1480 
1481  void export_support_files(const std::string &directory_name) const
1482  {
1483  const std::string sep(1, __pmPathSeparator());
1484  export_domain_header(directory_name + sep + "domain.h");
1485  export_help_text(directory_name + sep + "help");
1486  export_pmns_data(directory_name + sep + "pmns");
1487  export_pmns_root(directory_name + sep + "root");
1488  }
1489 
1490  inline std::pair<size_t, size_t> count_metrics(const metrics_description &metrics) const
1491  {
1492  std::set<const instance_domain *> instance_domains;
1493  size_t metric_count = 0;
1494  for (metrics_description::const_iterator metrics_iter = metrics.begin();
1495  metrics_iter != metrics.end(); ++metrics_iter)
1496  {
1497  const metric_cluster &cluster = metrics_iter->second;
1498  for (metric_cluster::const_iterator cluster_iter = cluster.begin();
1499  cluster_iter != cluster.end(); ++cluster_iter)
1500  {
1501  if (cluster_iter->second.domain != NULL) {
1502  instance_domains.insert(cluster_iter->second.domain);
1503  }
1504  metric_count++;
1505  }
1506  }
1507  return std::pair<size_t, size_t>(metric_count, instance_domains.size());
1508  }
1509 
1510  static inline pmdaIndom allocate_pmda_indom(const instance_domain &domain)
1511  {
1512  pmdaIndom indom;
1513  indom.it_indom = domain.get_domain_id();
1514  indom.it_numinst = domain.size();
1515  indom.it_set = new pmdaInstid [domain.size()];
1516  size_t index = 0;
1517  for (instance_domain::const_iterator iter = domain.begin(); iter != domain.end(); ++iter, ++index) {
1518  indom.it_set[index].i_inst = iter->first;
1519  indom.it_set[index].i_name = const_cast<char *>(iter->second.instance_name.c_str());
1520  }
1521  return indom;
1522  }
1523 
1524  static inline void validate_instance(const metric_description &description,
1525  const unsigned int instance)
1526  {
1527 #ifndef PCP_CPP_NO_ID_VALIDITY_CHECKS
1528  if (instance != PM_INDOM_NULL) {
1529  if (description.domain == NULL) {
1530  // Instance provided, but non required.
1531  throw pcp::exception(PM_ERR_INDOM);
1532  }
1533  if (description.domain->count(instance) <= 0) {
1534  // Instance provided, but not one we've registered.
1535  throw pcp::exception(PM_ERR_INST);
1536  }
1537  } else if (description.domain != NULL) {
1538  // Instance required, but none provided.
1539  throw pcp::exception(PM_ERR_INDOM);
1540  }
1541 #endif
1542  }
1543 
1544  /*
1545  * Static callback functions registered by the register_callbacks functions.
1546  * These all redirect thier non-static singleton counterparts above.
1547  */
1548 
1549 #if PCP_CPP_PMDA_INTERFACE_VERSION >= 6
1550  static int callback_attribute(int ctx, int attr, const char *value, int length, pmdaExt *pmda)
1551  {
1552  return get_instance()->on_attribute(ctx, attr, value, length, pmda);
1553  }
1554 #endif
1555 
1556 #if PCP_CPP_PMDA_INTERFACE_VERSION >= 4
1557  static int callback_children(const char *name, int traverse, char ***kids, int **sts, pmdaExt *pmda)
1558  {
1559  return get_instance()->on_children(name, traverse, kids, sts, pmda);
1560  }
1561 #endif
1562 
1563  static int callback_desc(pmID pmid, pmDesc *desc, pmdaExt *pmda)
1564  {
1565  return get_instance()->on_desc(pmid, desc, pmda);
1566  }
1567 
1568  static int callback_fetch(int numpmid, pmID *pmidlist, pmResult **resp, pmdaExt *pmda)
1569  {
1570  return get_instance()->on_fetch(numpmid, pmidlist, resp, pmda);
1571  }
1572 
1573  static int callback_fetch_callback(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *avp)
1574  {
1575  return get_instance()->on_fetch_callback(mdesc, inst, avp);
1576  }
1577 
1578  static int callback_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *pmda)
1579  {
1580  return get_instance()->on_instance(indom, inst, name, result, pmda);
1581  }
1582 
1583 #if PCP_CPP_PMDA_INTERFACE_VERSION >= 4
1584  static int callback_name(pmID pmid, char ***nameset, pmdaExt *pmda)
1585  {
1586  return get_instance()->on_name(pmid, nameset, pmda);
1587  }
1588 
1589  static int callback_pmid(const char *name, pmID *pmid, pmdaExt *pmda)
1590  {
1591  return get_instance()->on_pmid(name, pmid, pmda);
1592  }
1593 #endif
1594 
1595  static int callback_profile(__pmProfile *prof, pmdaExt *pmda)
1596  {
1597  return get_instance()->on_profile(prof, pmda);
1598  }
1599 
1600  static int callback_store(pmResult *result, pmdaExt *pmda)
1601  {
1602  return get_instance()->on_store(result, pmda);
1603  }
1604 
1605  static int callback_text(int ident, int type, char **buffer, pmdaExt *pmda)
1606  {
1607  return get_instance()->on_text(ident, type, buffer, pmda);
1608  }
1609 
1610 };
1611 
1612 } // pcp namespace.
1613 
1614 #ifndef PCP_CPP_SKIP_PCP_PMDA_INSTANCE_DEFINITION
1615 pcp::pmda * pcp::pmda::instance(NULL);
1616 #endif
1617 
1618 PCP_CPP_END_NAMESPACE
1619 
1620 #endif
unsigned int instance_id_type
https://github.com/pcolby/pcp-pmda-cpp/issues/11
Definition: types.hpp:25
cluster_id_type get_cluster_id() const
Get this cluster&#39;s ID.
virtual bool parse_command_line(const int argc, const char *const argv[], pmdaInterface &interface)
Parse command line options.
Definition: pmda.hpp:455
virtual std::string get_pmda_version() const
Get this PMDA&#39;s version string.
Definition: pmda.hpp:276
virtual void run_daemon(const int argc, char *const argv[])
Run this daemon.
Definition: pmda.hpp:293
Indentifies a metric to be fetched.
Definition: pmda.hpp:137
static void check_conflicting_options(const boost::program_options::variables_map &options_map, const std::string &option1, const std::string &option2)
Check for conflicting command line options.
Definition: pmda.hpp:726
virtual boost::program_options::positional_options_description get_supported_positional_options()
Get a list of supported positional options.
Definition: pmda.hpp:708
Basic instance domain information.
virtual int on_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *pmda)
Return description of instances and instance domains.
Definition: pmda.hpp:1104
int code
PMDA fetch code describing atom&#39;s memory allocation.
Definition: pmda.hpp:118
Definition: atom.hpp:20
std::string get_cluster_name() const
Get this cluster&#39;s name.
std::string metric_name
This metric&#39;s name.
item_id_type item
Item ID.
Definition: pmda.hpp:139
virtual ~pmda()
Destructor.
Definition: pmda.hpp:151
#define PCP_CPP_UNUSED(name)
Let the compiler know that a parameter is unsed.
Definition: config.hpp:106
static int run_daemon(const int argc, char *const argv[])
Run a PMDA in PCP&#39;s daemon mode.
Definition: pmda.hpp:91
virtual std::string get_help_text_pathname() const
Get the default path to this PMDA&#39;s optional help texts file.
Definition: pmda.hpp:217
Defines the pcp::instance_domain class.
virtual void store_value(const metric_id &metric, const int &value)
Store an in situ value.
Definition: pmda.hpp:975
virtual void display_help(const std::string &program_name) const
Display a help message.
Definition: pmda.hpp:747
pmAtomValue atom
Atom value.
Definition: pmda.hpp:117
#define PCP_CPP_BOOST_PO_VALUE_NAME(name)
Sets a command line option&#39;s value name, if supported by the version of Boost.Program_Options being b...
Definition: config.hpp:46
virtual void store_value(const metric_id &metric, const pmValueBlock *const value)
Store an ex situ value.
Definition: pmda.hpp:999
virtual bool parse_command_line(const int argc, const char *const argv[], pmdaInterface &interface, boost::program_options::variables_map &options)
Parse command line options.
Definition: pmda.hpp:479
A cluster of metric descriptions.
pmInDom get_pm_instance_domain() const
Get this instance domain&#39;s PCP-modifed ID.
Individual metric description.
metric_flags flags
Optional flags for this metric.
virtual int get_default_pmda_domain_number() const =0
Get this PMDA&#39;s default performance metrics domain number.
uint_fast8_t atom_type_type
PM_TYPE_* (0 - 9)
Definition: types.hpp:22
void *const opaque
Opaque value to track with this metric.
fetch_value_result(const pmAtomValue &atom, const int code=PMDA_FETCH_STATIC)
Constructor.
Definition: pmda.hpp:127
#define PCP_CPP_BOOST_PO_IMPLICIT_VALUE(...)
Sets a command line option&#39;s implicit value, if supported by the version of Boost.Program_Options being built against.
Definition: config.hpp:40
Sets up common PMDA++ library macros.
atom_type_type type
Expected atom type.
Definition: pmda.hpp:141
virtual void set_callbacks(pmdaInterface &interface)
Set static callbacks on a PMDA interface.
Definition: pmda.hpp:1255
virtual fetch_value_result fetch_value(const metric_id &metric)=0
Fetch an individual metric value.
void * opaque
Opaque pointer.
Definition: pmda.hpp:142
virtual int on_text(int ident, int type, char **buffer, pmdaExt *pmda)
Return the help text for the metric.
Definition: pmda.hpp:1192
virtual int on_desc(pmID pmid, pmDesc *desc, pmdaExt *pmda)
Return the metric desciption.
Definition: pmda.hpp:1036
virtual boost::program_options::options_description pcp_builtin_options() const
Get a list of command line options built into the PCP libraries.
Definition: pmda.hpp:611
std::string verbose_description
This metric&#39;s verbose description.
Base class for all PMDA++ exceptions.
Definition: exception.hpp:25
Metric supports pmstore operations.
#define PCP_CPP_PMDA_CONST_CHAR(str)
virtual int on_profile(__pmProfile *prof, pmdaExt *pmda)
Store the instance profile away for the next fetch.
Definition: pmda.hpp:1127
instance_domain * domain
Optional instance domain for this metric.
metrics_description supported_metrics
Definition: pmda.hpp:109
virtual void begin_fetch_values()
Begin fetching values.
Definition: pmda.hpp:929
virtual boost::program_options::options_description get_supported_hidden_options() const
Get a list of hidden supported command line options.
Definition: pmda.hpp:694
instance_id_type instance
Instance ID.
Definition: pmda.hpp:140
atom_type_type type
This metric&#39;s atom type.
virtual std::string get_config_file_pathname() const
Get the default path to this PMDA&#39;s optional configuration file.
Definition: pmda.hpp:202
Performance metric instance domain.
virtual void initialize_dso(pmdaInterface &interface)
Initialise a DSO interface with this PMDA.
Definition: pmda.hpp:816
void set_pm_instance_domain(const pmInDom domain)
Set this instance domain&#39;s PCP-modified ID.
std::vector< std::string > string_vector
A simple vector of strings.
Definition: pmda.hpp:146
virtual int error_code() const
Get this exception&#39;s error code.
Definition: exception.hpp:82
static pmda * get_instance()
Get the single PMDA instance.
Definition: pmda.hpp:166
uint_fast16_t item_id_type
__pmID_int::item (10-bits)
Definition: types.hpp:26
std::string verbose_description
Instance domain verbose description.
std::string short_description
This metric&#39;s short description.
virtual std::string get_usage(const std::string &program_name) const
Get a usage syntax string.
Definition: pmda.hpp:804
Defines the pcp::exception class.
Struct returned by the fetch_value function.
Definition: pmda.hpp:116
static void init_dso(pmdaInterface *const interface)
Initialize PCP&#39;s DSO interface for a PMDA.
Definition: pmda.hpp:60
virtual int on_fetch(int numpmid, pmID *pmidlist, pmResult **resp, pmdaExt *pmda)
Resize the pmResult and call e_callback in the pmdaExt structure for each metric instance required by...
Definition: pmda.hpp:1043
virtual pcp::metrics_description get_supported_metrics()=0
Get descriptions of all of the metrics supported by this PMDA.
cluster_id_type cluster
Cluster ID.
Definition: pmda.hpp:138
std::string short_description
Instance domain short description.
virtual int on_store(pmResult *result, pmdaExt *pmda)
Store a value into a metric.
Definition: pmda.hpp:1133
virtual int on_fetch_callback(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *avp)
Fetch the value of a single metric instance.
Definition: pmda.hpp:1056
Collection of clusters of metric descriptions.
Abstract base class for implementing PCP PMDAs.
Definition: pmda.hpp:37
virtual std::string get_log_file_pathname() const
Get the default path to this PMDA&#39;s log file.
Definition: pmda.hpp:231
atom_type_type type()
Get the PM_TYPE_* constant for a given C++ type.
virtual void display_version() const
Display a version message.
Definition: pmda.hpp:766
virtual boost::program_options::options_description get_supported_options() const
Get a list of command line options supported by this PMDA.
Definition: pmda.hpp:651
#define PCP_CPP_PMDA_INTERFACE_VERSION
PMDA interface version to use; defaults to "latest".
Definition: config.hpp:56
virtual void initialize_pmda(pmdaInterface &interface)
Initialise this PMDA.
Definition: pmda.hpp:845
domain_id_type get_domain_id() const
Get this instance domain&#39;s user-defined ID.
virtual std::string get_pmda_name() const =0
Get this PMDA&#39;s name.
virtual const char * what() const
Get this exception&#39;s error message.
Definition: exception.hpp:92
instance_id_type store(const pmInDom indom, const std::string &name, const int flags=PMDA_CACHE_ADD, void *const opaque=NULL)
Add a item to the cache.
Definition: cache.hpp:275
Defines various metric-description related classes.
uint_fast16_t cluster_id_type
__pmID_int::cluster (12-bits)
Definition: types.hpp:23
static pmda * set_instance(pmda *const new_instance)
Set the single PMDA instance.
Definition: pmda.hpp:185