pacemaker  2.1.9-49aab99839
Scalable High-Availability cluster resource manager
options_display.c
Go to the documentation of this file.
1 /*
2  * Copyright 2024 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 
12 #include <glib.h> // GSList, GString
13 
14 #include "crmcommon_private.h"
15 
23 static void
24 add_possible_values_default(pcmk__output_t *out,
25  const pcmk__cluster_option_t *option)
26 {
27  const char *id = _("Possible values");
28  GString *buf = g_string_sized_new(256);
29 
30  pcmk__assert(option->type != NULL);
31 
32  if (pcmk_is_set(option->flags, pcmk__opt_generated)) {
33  id = _("Possible values (generated by Pacemaker)");
34  }
35 
36  if ((option->values != NULL) && (strcmp(option->type, "select") == 0)) {
37  const char *delim = ", ";
38  bool found_default = (option->default_value == NULL);
39  char *str = pcmk__str_copy(option->values);
40 
41  for (const char *value = strtok(str, delim); value != NULL;
42  value = strtok(NULL, delim)) {
43 
44  if (buf->len > 0) {
45  g_string_append(buf, delim);
46  }
47  g_string_append_c(buf, '"');
48  g_string_append(buf, value);
49  g_string_append_c(buf, '"');
50 
51  if (!found_default && (strcmp(value, option->default_value) == 0)) {
52  found_default = true;
53  g_string_append(buf, _(" (default)"));
54  }
55  }
56  free(str);
57 
58  } else if (option->default_value != NULL) {
59  pcmk__g_strcat(buf,
60  option->type, _(" (default: \""), option->default_value,
61  "\")", NULL);
62 
63  } else {
64  pcmk__g_strcat(buf, option->type, _(" (no default)"), NULL);
65  }
66 
67  out->list_item(out, id, "%s", buf->str);
68  g_string_free(buf, TRUE);
69 }
70 
78 static void
79 add_option_metadata_default(pcmk__output_t *out,
80  const pcmk__cluster_option_t *option)
81 {
82  const char *desc_short = option->description_short;
83  const char *desc_long = option->description_long;
84 
85  pcmk__assert((desc_short != NULL) || (desc_long != NULL));
86 
87  if (desc_short == NULL) {
88  desc_short = desc_long;
89  desc_long = NULL;
90  }
91 
92  out->list_item(out, option->name, "%s", _(desc_short));
93 
94  out->begin_list(out, NULL, NULL, NULL);
95 
96  if (desc_long != NULL) {
97  out->list_item(out, NULL, "%s", _(desc_long));
98  }
99  add_possible_values_default(out, option);
100  out->end_list(out);
101 }
102 
123 PCMK__OUTPUT_ARGS("option-list", "const char *", "const char *", "const char *",
124  "uint32_t", "const pcmk__cluster_option_t *", "bool")
125 static int
126 option_list_default(pcmk__output_t *out, va_list args)
127 {
128  const char *name G_GNUC_UNUSED = va_arg(args, const char *);
129  const char *desc_short = va_arg(args, const char *);
130  const char *desc_long = va_arg(args, const char *);
131  const uint32_t filter = va_arg(args, uint32_t);
132  const pcmk__cluster_option_t *option_list =
133  va_arg(args, pcmk__cluster_option_t *);
134  const bool all = (bool) va_arg(args, int);
135 
136  const bool show_deprecated = all
137  || pcmk_is_set(filter, pcmk__opt_deprecated);
138  const bool show_advanced = all || pcmk_is_set(filter, pcmk__opt_advanced);
139  bool old_fancy = false;
140 
141  GSList *deprecated = NULL;
142  GSList *advanced = NULL;
143 
144  pcmk__assert((out != NULL) && (desc_short != NULL)
145  && (desc_long != NULL) && (option_list != NULL));
146 
147  old_fancy = pcmk__output_text_get_fancy(out);
148  pcmk__output_text_set_fancy(out, true);
149 
150  out->info(out, "%s", _(desc_short));
151  out->spacer(out);
152  out->info(out, "%s", _(desc_long));
153  out->begin_list(out, NULL, NULL, NULL);
154 
155  for (const pcmk__cluster_option_t *option = option_list;
156  option->name != NULL; option++) {
157 
158  // Store deprecated and advanced options to display later if appropriate
159  if (pcmk_all_flags_set(option->flags, filter)) {
160  if (pcmk_is_set(option->flags, pcmk__opt_deprecated)) {
161  if (show_deprecated) {
162  deprecated = g_slist_prepend(deprecated, (gpointer) option);
163  }
164 
165  } else if (pcmk_is_set(option->flags, pcmk__opt_advanced)) {
166  if (show_advanced) {
167  advanced = g_slist_prepend(advanced, (gpointer) option);
168  }
169 
170  } else {
171  out->spacer(out);
172  add_option_metadata_default(out, option);
173  }
174  }
175  }
176 
177  if (advanced != NULL) {
178  advanced = g_slist_reverse(advanced);
179 
180  out->spacer(out);
181  out->begin_list(out, NULL, NULL, _("ADVANCED OPTIONS"));
182  for (const GSList *iter = advanced; iter != NULL; iter = iter->next) {
183  const pcmk__cluster_option_t *option = iter->data;
184 
185  out->spacer(out);
186  add_option_metadata_default(out, option);
187  }
188  out->end_list(out);
189  g_slist_free(advanced);
190  }
191 
192  if (deprecated != NULL) {
193  deprecated = g_slist_reverse(deprecated);
194 
195  out->spacer(out);
196  out->begin_list(out, NULL, NULL,
197  _("DEPRECATED OPTIONS (will be removed in a future "
198  "release)"));
199  for (const GSList *iter = deprecated; iter != NULL; iter = iter->next) {
200  const pcmk__cluster_option_t *option = iter->data;
201 
202  out->spacer(out);
203  add_option_metadata_default(out, option);
204  }
205  out->end_list(out);
206  g_slist_free(deprecated);
207  }
208 
209  out->end_list(out);
210  pcmk__output_text_set_fancy(out, old_fancy);
211  return pcmk_rc_ok;
212 }
213 
226 static void
227 add_desc_xml(pcmk__output_t *out, bool for_long, const char *desc)
228 {
229  const char *tag = (for_long? PCMK_XE_LONGDESC : PCMK_XE_SHORTDESC);
230  xmlNode *node = pcmk__output_create_xml_text_node(out, tag, desc);
231 
233 
234 #ifdef ENABLE_NLS
235  {
236  static const char *locale = NULL;
237 
238  if (strcmp(desc, _(desc)) == 0) {
239  return;
240  }
241 
242  if (locale == NULL) {
243  locale = strtok(setlocale(LC_ALL, NULL), "_");
244  }
245  node = pcmk__output_create_xml_text_node(out, tag, _(desc));
246  crm_xml_add(node, PCMK_XA_LANG, locale);
247  }
248 #endif
249 }
250 
260 static void
261 add_possible_values_xml(pcmk__output_t *out,
262  const pcmk__cluster_option_t *option)
263 {
264  if ((option->values != NULL) && (strcmp(option->type, "select") == 0)) {
265  const char *delim = ", ";
266  char *str = pcmk__str_copy(option->values);
267  const char *ptr = strtok(str, delim);
268 
269  while (ptr != NULL) {
271  PCMK_XA_VALUE, ptr,
272  NULL);
273  ptr = strtok(NULL, delim);
274  }
275  free(str);
276  }
277 }
278 
287 static const char *
288 map_legacy_option_type(const char *type)
289 {
290  // @COMPAT Drop this function when we drop daemon metadata commands
292  return PCMK__VALUE_TIME;
293 
294  } else if (pcmk__str_any_of(type,
296  PCMK_VALUE_SCORE, NULL)) {
297  return PCMK_VALUE_INTEGER;
298 
299  } else if (pcmk__str_eq(type, PCMK_VALUE_VERSION, pcmk__str_none)) {
300  return PCMK_VALUE_STRING;
301 
302  } else {
303  return type;
304  }
305 }
306 
314 static void
315 add_option_metadata_xml(pcmk__output_t *out,
316  const pcmk__cluster_option_t *option)
317 {
318  const char *type = option->type;
319  const char *desc_long = option->description_long;
320  const char *desc_short = option->description_short;
321  const bool advanced = pcmk_is_set(option->flags, pcmk__opt_advanced);
322  const bool deprecated = pcmk_is_set(option->flags, pcmk__opt_deprecated);
323  const bool generated = pcmk_is_set(option->flags, pcmk__opt_generated);
324 
325  // OCF requires "1"/"0" and does not allow "true"/"false
326  // @COMPAT Variables no longer needed after we drop legacy mode
327  const char *advanced_s = advanced? "1" : "0";
328  const char *generated_s = generated? "1" : "0";
329 
330  // @COMPAT For daemon metadata only; drop when daemon metadata is dropped
331  const bool legacy = pcmk__output_get_legacy_xml(out);
332  char *desc_long_legacy = NULL;
333  GString *desc_short_legacy = NULL;
334 
335  // The standard requires a parameter type
336  pcmk__assert(type != NULL);
337 
338  // The standard requires long and short parameter descriptions
339  pcmk__assert((desc_long != NULL) || (desc_short != NULL));
340 
341  if (desc_long == NULL) {
342  desc_long = desc_short;
343  } else if (desc_short == NULL) {
344  desc_short = desc_long;
345  }
346 
347  if (legacy) {
348  // This is ugly but it will go away at a major release bump
349  type = map_legacy_option_type(type);
350 
351  if (option->values != NULL) {
352  desc_long_legacy = crm_strdup_printf("%s Allowed values: %s",
353  desc_long, option->values);
354  desc_long = desc_long_legacy;
355  }
356 
357  if (deprecated || advanced) {
358  const size_t init_sz = 1023;
359 
360  if (desc_long != option->description_long) {
361  /* desc_long was NULL and got assigned desc_short, which was
362  * non-empty. Let desc_long have the "real" description, and put
363  * the flag in desc_short.
364  */
365  desc_short = "";
366  } else {
367  desc_short = pcmk__s(option->description_short, "");
368  }
369 
370  if (deprecated) {
371  pcmk__add_separated_word(&desc_short_legacy, init_sz,
372  "*** Deprecated ***", NULL);
373  }
374  if (advanced) {
375  pcmk__add_separated_word(&desc_short_legacy, init_sz,
376  "*** Advanced Use Only ***", NULL);
377  }
378  pcmk__add_separated_word(&desc_short_legacy, 0, desc_short, NULL);
379 
380  desc_short = desc_short_legacy->str;
381  }
382 
383  /* These must be NULL when used as attribute values later.
384  * PCMK_XA_ADVANCED and PCMK_XA_GENERATED break validation for some
385  * legacy tools.
386  */
387  advanced_s = NULL;
388  generated_s = NULL;
389  }
390 
392  PCMK_XA_NAME, option->name,
393  PCMK_XA_ADVANCED, advanced_s,
394  PCMK_XA_GENERATED, generated_s,
395  NULL);
396 
397  if (deprecated && !legacy) {
398  // No need yet to support "replaced-with" or "desc"; add if needed
400  }
401  add_desc_xml(out, true, desc_long);
402  add_desc_xml(out, false, desc_short);
403 
407  NULL);
408 
409  add_possible_values_xml(out, option);
410 
413 
414  free(desc_long_legacy);
415  if (desc_short_legacy != NULL) {
416  g_string_free(desc_short_legacy, TRUE);
417  }
418 }
419 
438 PCMK__OUTPUT_ARGS("option-list", "const char *", "const char *", "const char *",
439  "uint32_t", "const pcmk__cluster_option_t *", "bool")
440 static int
441 option_list_xml(pcmk__output_t *out, va_list args)
442 {
443  const char *name = va_arg(args, const char *);
444  const char *desc_short = va_arg(args, const char *);
445  const char *desc_long = va_arg(args, const char *);
446  const uint32_t filter = va_arg(args, uint32_t);
447  const pcmk__cluster_option_t *option_list =
448  va_arg(args, pcmk__cluster_option_t *);
449 
450  pcmk__assert((out != NULL) && (name != NULL) && (desc_short != NULL)
451  && (desc_long != NULL) && (option_list != NULL));
452 
456  NULL);
457 
459  add_desc_xml(out, true, desc_long);
460  add_desc_xml(out, false, desc_short);
461 
463 
464  for (const pcmk__cluster_option_t *option = option_list;
465  option->name != NULL; option++) {
466 
467  if (pcmk_all_flags_set(option->flags, filter)) {
468  add_option_metadata_xml(out, option);
469  }
470  }
471 
474  return pcmk_rc_ok;
475 }
476 
477 static pcmk__message_entry_t fmt_functions[] = {
478  { "option-list", "default", option_list_default },
479  { "option-list", "xml", option_list_xml },
480 
481  { NULL, NULL, NULL }
482 };
483 
490 void
492  pcmk__register_messages(out, fmt_functions);
493 }
void(* end_list)(pcmk__output_t *out)
#define PCMK_XA_DEFAULT
Definition: xml_names.h:259
#define PCMK_XA_NAME
Definition: xml_names.h:330
void pcmk__register_messages(pcmk__output_t *out, const pcmk__message_entry_t *table)
Definition: output.c:204
const char * name
Definition: cib.c:26
PCMK__OUTPUT_ARGS("option-list", "const char *", "const char *", "const char *", "uint32_t", "const pcmk__cluster_option_t *", "bool")
#define PCMK_XA_GENERATED
Definition: xml_names.h:293
bool pcmk__output_get_legacy_xml(pcmk__output_t *out)
Definition: output_xml.c:593
xmlNodePtr pcmk__output_xml_create_parent(pcmk__output_t *out, const char *name,...) G_GNUC_NULL_TERMINATED
Definition: output_xml.c:478
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:313
#define PCMK_XE_PARAMETERS
Definition: xml_names.h:159
void pcmk__add_separated_word(GString **list, size_t init_size, const char *word, const char *separator)
Definition: strings.c:806
Option is deprecated.
enum crm_ais_msg_types type
Definition: cpg.c:51
#define PCMK_XE_LONGDESC
Definition: xml_names.h:129
#define PCMK_VALUE_SCORE
Definition: options.h:202
#define PCMK_XE_DEPRECATED
Definition: xml_names.h:99
#define PCMK_XA_TYPE
Definition: xml_names.h:430
#define PACEMAKER_VERSION
Definition: config.h:517
G_GNUC_INTERNAL bool pcmk__output_text_get_fancy(pcmk__output_t *out)
Definition: output_text.c:372
#define PCMK_VALUE_TIMEOUT
Definition: options.h:214
Advanced use only.
#define PCMK_XA_ADVANCED
Definition: xml_names.h:233
#define PCMK_XE_VERSION
Definition: xml_names.h:220
#define PCMK__VALUE_EN
#define PCMK_VALUE_DURATION
Definition: options.h:147
#define PCMK_XE_PARAMETER
Definition: xml_names.h:158
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1308
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:94
#define PCMK__VALUE_TIME
xmlNodePtr pcmk__output_create_xml_node(pcmk__output_t *out, const char *name,...) G_GNUC_NULL_TERMINATED
Definition: output_xml.c:515
#define PCMK_VALUE_NONNEGATIVE_INTEGER
Definition: options.h:179
#define _(String)
Definition: crm_internal.h:57
#define PCMK_XA_VALUE
Definition: xml_names.h:442
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1062
#define pcmk__str_copy(str)
void pcmk__output_xml_pop_parent(pcmk__output_t *out)
Definition: output_xml.c:562
#define PCMK_XE_SHORTDESC
Definition: xml_names.h:199
#define pcmk__assert(expr)
#define PCMK_XE_RESOURCE_AGENT
Definition: xml_names.h:173
#define PCMK_XE_OPTION
Definition: xml_names.h:152
#define PCMK_XE_CONTENT
Definition: xml_names.h:90
void(*) void(* list_item)(pcmk__output_t *out, const char *name, const char *format,...) G_GNUC_PRINTF(3
This structure contains everything that makes up a single output formatter.
void(* begin_list)(pcmk__output_t *out, const char *singular_noun, const char *plural_noun, const char *format,...) G_GNUC_PRINTF(4
const char * description_short
#define PCMK_VALUE_VERSION
Definition: options.h:219
void pcmk__output_text_set_fancy(pcmk__output_t *out, bool enabled)
Definition: output_text.c:395
#define PCMK_OCF_VERSION
Definition: agents.h:54
uint32_t flags
Group of enum pcmk__opt_flags
#define PCMK_VALUE_STRING
Definition: options.h:208
#define PCMK_VALUE_INTEGER
Definition: options.h:164
#define PCMK_XA_LANG
Definition: xml_names.h:313
void pcmk__register_option_messages(pcmk__output_t *out)
xmlNodePtr pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const char *content)
Definition: output_xml.c:536
Generated by Pacemaker.
#define PCMK_XA_VERSION
Definition: xml_names.h:444
const char * description_long