PMGETOPTIONS(3)

🌸 PMGETOPTIONS(3) β€” the PMAPI command line glow-up πŸ’…

Library Functions Manual Β· part of the PCP crew


✨ NAME ✨

pmgetopt_r, pmGetOptions, pmGetContextOptions, pmFreeOptions, pmUsageMessage β€” command line handling for PMAPI tools πŸŽ€


πŸ¦‹ C SYNOPSIS πŸ¦‹

#include <pcp/pmapi.h>

int pmgetopt_r(int argc, char *const *argv, pmOptions *opts);
int pmGetOptions(int argc, char *const *argv, pmOptions *opts);
int pmGetContextOptions(int ctx, pmOptions *opts);
void pmUsageMessage(pmOptions *opts);
void pmFreeOptions(pmOptions *opts);

cc ... -lpcp

πŸ’– DESCRIPTION πŸ’–

okay bestie, sit down, because pmGetOptions is literally the moment. it hands you command line option processing services for both monitor and collector PMAPI(3) tools, like it's not even picky. it's modelled on the thread-safe variants of the GNU getopt_long(3) API, and the main way it differs is that it's obsessed (in the best way) with generalised processing for the de-facto standard PCP command line options described in PCPIntro(1). we're talking host and archive specification, time windows, timezones, sample counts, time intervals, and so on. the whole standard-options starter pack. ✨

the main character here is pmGetOptions, and you should pass it the argc argument count and the argv array exactly as they arrived in your main() function on program invocation. the final opts argument is the one that describes the set of long and short options your tool is prepared to process, plus all the other metadata about how those options should be processed. it's giving "here's the menu, now let's cook". πŸ‘‘

the pmgetopt_r interface β€” which pmGetOptions uses internally β€” behaves in a similar fashion, but ngl, it does not perform any common option processing. so it's more suited to PCP collector processes, whereas PCP monitor tools tend to reach for pmGetOptions. iykyk. πŸ’…

the opts argument consists of an array of pmLongOpts entries describing the arguments, plus the enclosing pmOptions struct, and they're defined like this (internal fields are not presented, for brevity):

typedef struct {
    const char *        long_opt;
    int                 has_arg;
    int                 short_opt;
    const char *        argname;
    const char *        message;
} pmLongOptions;

typedef struct {
    int                 version;
    int                 flags;
    const char *        short_options;
    pmLongOptions *     long_options;
    const char *        short_usage;
    pmOptionOverride    override;

    int                 index;
    int                 optind;
    int                 opterr;
    int                 optopt;
    char                *optarg;
    /* [internal fields, undocumented] */

    int                 errors;
    int                 context; /* PM_CONTEXT_{HOST,ARCHIVE,LOCAL} */
    int                 nhosts;
    int                 narchives;
    char **             hosts;
    char **             archives;
    struct timespec     start;
    struct timespec     finish;
    struct timespec     origin;
    struct timespec     interval;
    char *              align_optarg;
    char *              start_optarg;
    char *              finish_optarg;
    char *              origin_optarg;
    char *              guiport_optarg;
    char *              timezone;
    int                 samples;
    int                 guiport;
    int                 padding;
    unsigned int        guiflag : 1;
    unsigned int        tzflag  : 1;
    unsigned int        nsflag  : 1;
    unsigned int        Lflag   : 1;
    unsigned int        zeroes  : 28;
} pmOptions;

the initial flags and version fields describe how the rest of the pmOptions structure is to be interpreted, okay? these fields can be zeroed, which specifies a default interpretation. alternatively, the PMAPI_VERSION macro can be used to specify the API level to use (currently, values of 3 or less are allowed, so don't get any ideas 🌈). Version 2 is the default, and version 3 introduces high resolution time window and interval fields β€” that's struct timespec as opposed to struct timeval, for the record. when you're using that latter form, before including <pcp/pmapi.h> the PMAPI_VERSION macro must be set to 3 to ensure the correct layout of the pmOptions structure is used by the application. do not skip that step, i am begging. 😭 the flags field can be used to modify option processing behaviour as described in the FLAGS VALUES section below.

the array of long_options pmLongOpts structures must be terminated by a sentinel, and the PMAPI_OPTIONS_END macro can be used to effect this termination. individual records within the long_options array can be of two types β€” options headers, or actual options. an options header is constructed using the PMAPI_OPTIONS_HEADER macro, and it's used for usage message option grouping. free form text can be inserted into the usage message at any point using the PMAPI_OPTIONS_TEXT macro β€” this one is intended for additional explanatory text covering detailed usage that is beyond the scope of the individual headers or options. otherwise, the array entry specifies an option, and those should:

  • be named (long_opt) if a long-option form is allowed,
  • specify whether or not they take an argument (has_arg),
  • specify the single character variant argument (short_opt) if a short-option form is allowed,
  • and finally specify how to present the option in the usage message.

that last component consists of a short, one-word description of the optional argument (argname) and a one-line description of what the command-line option does (message). cute and functional, we love to see it. 🫢

the short_usage string is also used only when constructing the usage message. it forms the component of the usage message that follows the program name (i.e. argv[0]).

the optional short_options string is the normal getopt command-line option specification string, using individual characters β€” those with arguments are designated as such using the ':' character β€” as used by all getopt implementations. classic. πŸ’€

a facility is provided to extend the existing set of common options with additional options, as well as to re-task the standard options into non-standard roles for individual tools. the latter is achieved using the override method, which allows a callback function to be provided that will be called on receipt of every argument, prior to common processing. if this callback returns a non-zero value, the common processing will be short-circuited for that option; otherwise, processing continues. so each client tool is free to choose exactly which of the standard options they wish to support β€” this can be all, some, or none, and no matter what they choose, each tool always has access to the long option parsing capability and the usage message generation facility. main character energy for every single tool, no cap. πŸ‘‘

the remaining pmOptions structure fields are filled in as a result of processing the arguments, and they're largely self-explanatory. further discussion of these is deferred to the FLAGS VALUES section below. the errors field contains a count of errors detected during option processing. these can be either usage or runtime errors, as indicated by the flags field (set, and passed out to the caller). typically, a command line tool will fail to start successfully and will produce an error message (e.g. via pmUsageMessage) if the errors field is non-zero at the end of either pmGetOptions or pmGetContextOptions. so like, check it, bestie. πŸ™ˆ

some command line option post-processing can only be performed once the tool has established a PMAPI context via pmNewContext(3). this processing includes use of context-aware timezones (-z), and time window processing (-A, -O, -S, -T) that may be affected by the timezone, for example. the pmGetContextOptions function is available for exactly those situations, and it completes any remaining processing of opts with respect to the ctx context identifier given. ✨

the pmUsageMessage function generates a usage message for the tool, and it includes both standard PCP options and custom options for each tool, as specified by the pmLongOptions array. it supports grouping of options (via PMAPI_OPTIONS_HEADER) as well as neat formatting of all options β€” short and long β€” their arguments, and individual explanatory messages. it will build this usage message using pmprintf(3), upon which it will issue a single pmflush(3) before returning to the caller, provided the PM_OPTFLAG_USAGE_ERR flag is set in flags β€” which will happen automatically during option parsing, when usage errors are detected. it's giving "i already knew you'd mess up, and i formatted a whole message about it". πŸ’…

lowkey important: in certain situations, such as recording lists of host specifications or PCP archive paths, the pmGetOptions routine may allocate memory, and store pointers to it within opts. should a program wish to free this memory before exiting, it can use the pmFreeOptions routine to do so. this is safe to call irrespective of whether memory was allocated dynamically, provided that opts was zeroed initially. ✨

…and that's only part 1, bestie, there's more where that came from πŸŽ€

🌸 FLAGS VALUES 🌸

okay bestie, here's the whole flag fam πŸ’… each one is a little switch you can flip, and they do very specific things, so let's go one by one:

  • PM_OPTFLAG_INIT β€” this one is a library-internal lil thing. It's there to mark that initialisation has already happened, so on the next calls it won't get done all over again. iykyk ✨
  • PM_OPTFLAG_DONE β€” mostly an internal flag too, and it means the final option processing is officially wrapped up. That processing? It cross-references a bunch of the options β€” checking for mutual exclusion, for example. There might be other post-processing happening at this stage too, as long as it doesn't need a PMAPI context. no cap πŸ”₯
  • PM_OPTFLAG_MULTI β€” this lets you specify more than one host or set of archives. Default vibe is one source of metrics only, but some of the more sophisticated tools allow multiple metric sources, and each one gets handled inside its own separate context. also peep PM_OPTFLAG_MIXED πŸ‘€
  • PM_OPTFLAG_USAGE_ERR β€” tells you the library caught a command-line usage error. we're talking stuff like an option needing an argument but getting none, or conflicting options being specified (like -s and -T, which is giving chaos 😭).
  • PM_OPTFLAG_RUNTIME_ERR β€” the library spotted an error at run time. this is like failing to retrieve timezone info from pmcd(1), or failing to load an alternate metric namespace from a local file (via the -n option). not the vibe πŸ’€
  • PM_OPTFLAG_EXIT β€” this is the library gently suggesting your tool should exit cleanly. it gets used when the version number is requested, for example (that's the -V option and the PMOPT_VERSION macro). bye bestie, on purpose 🫢
  • PM_OPTFLAG_POSIX β€” strict POSIX command line argument handling, periodt. that means options and the arguments that follow them will not be reordered, so you can't put additional options after command line arguments. this can matter a lot for tools where the arguments can be negative numbers, for example, because those should not be treated as command line options in that case. important!! ⭐
  • PM_OPTFLAG_MIXED β€” allows both live and archive metric sources to be specified. default is one type of metric context only, but some of the more sophisticated tools permit multiple context types. see also PM_OPTFLAG_MULTI πŸ’–
  • PM_OPTFLAG_ENV_ONLY β€” so many options can be specified either through the command line or from similarly-named environment variables. this flag disables all argument parsing, and only changes opts based on the environment variables. lowkey useful for tools that want to make sure no command line option conflicts pop up between their own set and the standard PCP option set (think an existing tool, reimplemented using PMAPI services). iconic πŸŽ€
  • PM_OPTFLAG_LONG_ONLY β€” only process long options. short options? not invited. 🚫
  • PM_OPTFLAG_BOUNDARIES β€” default pmGetOptions behaviour is to parse the time window options (that's -A, -O, -S and -T) only if one of those options was specified on the command line. but this flag lets you (especially with archive contexts) find the start and finish times associated with the context(s) even when no time window options were specified. and with multiple archives? the time window is defined as the time window spanning all of the archives. slay 🌈
  • PM_OPTFLAG_STDOUT_TZ β€” the timezone being used gets reported on the standard output stream during option parsing. default behaviour is to not report it, and instead just return timezone info via the timezone (-Z) and tzflag (-z) fields in the opts structure. ✨
  • PM_OPTFLAG_NOFLUSH β€” if this flag is set, the final pmflush call issued by pmUsageMessage gets skipped. super handy when the caller wants to append additional test to the generated usage message before flushing. πŸ¦‹
  • PM_OPTFLAG_QUIET β€” suppresses the messages from pmgetopt_r about unrecognised command line options. this is equivalent to setting the opterr field in the opt parameter (which mimics the getopt variable of the same name). peace and quiet, we love to see it 🀫 πŸ’œ

🌈 OPTIONS VIA ENVIRONMENT VARIABLES 🌈

okay bestie, plot twist: some of these options can also be set through environment variables instead of typing flags on the command line. πŸ’…

but listen β€” this mechanism is primarily for internal use by PCP tools. if you're a regular human, pleeease just use the command line options. fr fr. they show your intent way more clearly, make debugging like a million times easier, and save you from the absolute chaos of a command line option fighting an environment variable (and yeah, when they throw hands, the command line options usually ``win''). πŸ‘‘

here's the rundown of every environment variable that can stand in for a command line option, with its short option, long option and what it actually means:

  • $PCP_ALIGN_TIME β€” short option -A, long option --align β€” align sample times on natural boundaries ✨
  • $PCP_ARCHIVE β€” short option -a, long option --archive β€” metrics source is a PCP archive πŸ“¦
  • $PCP_ARCHIVE_LIST β€” no short option, long option --archive list β€” a comma-separated list of metric source archives
  • $PCP_FOLIO β€” no short option, long option --archive-folio β€” the metric source is a mkaf(1) archives folio
  • $PCP_DEBUG β€” short option -D, long option --debug β€” a comma-separated list of pmSetDebug(3) debugging options πŸ›
  • $PCP_GUIMODE β€” short option -g, long option --guimode β€” start in GUI mode with new pmtime(1) time control πŸ–₯️
  • $PCP_HOST β€” short option -h, long option --host β€” metrics source is pmcd(1) on a host 🌐
  • $PCP_HOST_LIST β€” no short option, long option --host-list β€” a comma-separated list of metric source hosts
  • $PCP_SPECLOCAL β€” short option -K, long option --spec-local β€” optional extra local context PMDA DSO information
  • $PCP_LOCALPMDA or $PCP_LOCALMODE β€” short option -L, long option --local-PMDA β€” metrics source is a local connection to a DSO PMDA 🫢
  • $PCP_NAMESPACE β€” short option -n, long option --namespace β€” use alternative PMNS (Perform-ance Metrics Name Space)
  • $PCP_UNIQNAMES β€” short option -N, long option --uniqnames β€” like -n but only one name allowed for each metric in the PMNS
  • $PCP_ORIGIN or $PCP_ORIGIN_TIME β€” short option -O, long option --origin β€” initial sample time within the time window ⏰
  • $PCP_GUIPORT β€” short option -p, long option --guiport β€” port of existing pmtime(1) time control
  • $PCP_START_TIME β€” short option -S, long option --start β€” start of the time window
  • $PCP_SAMPLES β€” short option -s, long option --samples β€” terminate after this many samples πŸ”’
  • $PCP_FINISH_TIME β€” short option -T, long option --finish β€” end of the time window
  • $PCP_INTERVAL β€” short option -t, long option --interval β€” sample interval ⏱️
  • $PCP_TIMEZONE β€” short option -Z, long option --timezone β€” set reporting timezone 🌍
  • $PCP_HOSTZONE β€” short option -z, long option --hostzone β€” set reporting timezone to local time of metrics source πŸ•°οΈ

πŸ’« COMPATIBILITY πŸ’«

history lesson time, bestie, don't scroll past this one 🫣

back before PCP 7.0 and libpcp.so.4, the start, finish, origin and interval members of the pmOptions struct were struct timevals. wild, right? 😭

so to support the PMAPI transition, the old interfaces and semantics can still be used if applications are linked with libpcp.so.3 or recompiled with -DPMAPI_VERSION=2. πŸ“œ

✨ RETURN VALUE ✨

okay bestie, here's the tea on what these functions hand back to you 🫢

pmGetOptions returns either when it spots a command-line option that is not one of the standard PCP set, or when it hits the end of the command line options β€” and at that point -1 gets returned. that's it, that's the whole exit strategy.

both pmgetopt_r and pmGetOptions give control back to the caller in exactly the same way a regular getopt call would, with the return value telling you one of three things:

  • the end of all processing (-1) πŸ’€
  • the single character form of the option currently being processed
  • zero, for the special long-option-only case

and for all the option-processing cases, the opts structure comes back containing filled out optarg, opterr, optopt, optind, and index fields as normal.

⚠️ bestie do NOT use the global optarg or optind from your platform C library β€” those will NOT be modified. like, at all. use the struct. i'm begging. 😭

pmGetOptions does not return to the caller when any of the standard PCP options are being processed (although the override mechanism can be used to still detect such options if needed). so if you're sitting there waiting for a return that never comes... that's why. πŸ™ˆ

pmGetContextOptions is the chill one β€” it returns zero on success, or a negative PCP error code on failure. and in that failure case, the error field within the opts parameter will also be non-zero. so like, two places to check, both valid, no cap. ✨


🌸 PCP ENVIRONMENT 🌸

environment variables with the prefix PCP_ are used to parameterize the file and directory names used by PCP. it's giving "everything is configurable, we love a flexible queen" πŸ’…

on each installation, the file /etc/pcp.conf contains the local values for these variables. so that's where the ✨lore✨ lives.

the $PCP_CONF variable may be used to specify an alternative configuration file, as described in pcp.conf(5). yes, you can point it somewhere else, you're the main character, go off πŸ‘‘

and values for these variables may be obtained programmatically using the pmGetOptions(3) function. so it's a whole little ecosystem, iykyk. 🌈


πŸ¦‹ SEE ALSO πŸ¦‹

go read these next, they're iconic, they're all part of the same friend group fr fr:


and that's the whole page bestie!! πŸŽ€ this one is brought to you by Performance Co-Pilot (that's the source) and PCP (that's the version/collection), page PMGETOPTIONS(3). you ate this, genuinely. go write some option-parsing code that actually respects its opts struct. periodt. πŸ’–βœ¨πŸ«Ά

πŸ“¦ where this page comes from