pacemaker  2.1.8-3980678f03
Scalable High-Availability cluster resource manager
output.c
Go to the documentation of this file.
1 /*
2  * Copyright 2019-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 <crm/common/util.h>
13 #include <crm/common/xml.h>
14 #include <libxml/tree.h>
15 
16 #include "crmcommon_private.h"
17 
18 static GHashTable *formatters = NULL;
19 
20 #if defined(PCMK__UNIT_TESTING)
21 // LCOV_EXCL_START
22 GHashTable *
23 pcmk__output_formatters(void) {
24  return formatters;
25 }
26 // LCOV_EXCL_STOP
27 #endif
28 
29 void
31  if (out == NULL) {
32  return;
33  }
34 
35  out->free_priv(out);
36 
37  if (out->messages != NULL) {
38  g_hash_table_destroy(out->messages);
39  }
40 
41  g_free(out->request);
42  free(out);
43 }
44 
62 int
63 pcmk__bare_output_new(pcmk__output_t **out, const char *fmt_name,
64  const char *filename, char **argv)
65 {
66  pcmk__output_factory_t create = NULL;
67 
68  CRM_ASSERT(formatters != NULL && out != NULL);
69 
70  /* If no name was given, just try "text". It's up to each tool to register
71  * what it supports so this also may not be valid.
72  */
73  if (fmt_name == NULL) {
74  create = g_hash_table_lookup(formatters, "text");
75  } else {
76  create = g_hash_table_lookup(formatters, fmt_name);
77  }
78 
79  if (create == NULL) {
81  }
82 
83  *out = create(argv);
84  if (*out == NULL) {
85  return ENOMEM;
86  }
87 
88  if (pcmk__str_eq(filename, "-", pcmk__str_null_matches)) {
89  (*out)->dest = stdout;
90  } else {
91  (*out)->dest = fopen(filename, "w");
92  if ((*out)->dest == NULL) {
93  pcmk__output_free(*out);
94  *out = NULL;
95  return errno;
96  }
97  }
98 
99  (*out)->quiet = false;
100  (*out)->messages = pcmk__strkey_table(free, NULL);
101 
102  if ((*out)->init(*out) == false) {
103  pcmk__output_free(*out);
104  return ENOMEM;
105  }
106 
107  setenv("OCF_OUTPUT_FORMAT", (*out)->fmt_name, 1);
108 
109  return pcmk_rc_ok;
110 }
111 
112 int
113 pcmk__output_new(pcmk__output_t **out, const char *fmt_name,
114  const char *filename, char **argv)
115 {
116  int rc = pcmk__bare_output_new(out, fmt_name, filename, argv);
117 
118  if (rc == pcmk_rc_ok) {
119  // Register libcrmcommon messages
122  }
123  return rc;
124 }
125 
126 int
127 pcmk__register_format(GOptionGroup *group, const char *name,
128  pcmk__output_factory_t create,
129  const GOptionEntry *options)
130 {
131  char *name_copy = NULL;
132 
133  CRM_ASSERT(create != NULL && !pcmk__str_empty(name));
134 
135  name_copy = strdup(name);
136  if (name_copy == NULL) {
137  return ENOMEM;
138  }
139 
140  if (formatters == NULL) {
141  formatters = pcmk__strkey_table(free, NULL);
142  }
143 
144  if (options != NULL && group != NULL) {
145  g_option_group_add_entries(group, options);
146  }
147 
148  g_hash_table_insert(formatters, name_copy, create);
149  return pcmk_rc_ok;
150 }
151 
152 void
153 pcmk__register_formats(GOptionGroup *group,
154  const pcmk__supported_format_t *formats)
155 {
156  if (formats == NULL) {
157  return;
158  }
159  for (const pcmk__supported_format_t *entry = formats; entry->name != NULL;
160  entry++) {
161  pcmk__register_format(group, entry->name, entry->create, entry->options);
162  }
163 }
164 
165 void
167  if (formatters != NULL) {
168  g_hash_table_destroy(formatters);
169  formatters = NULL;
170  }
171 }
172 
173 int
174 pcmk__call_message(pcmk__output_t *out, const char *message_id, ...) {
175  va_list args;
176  int rc = pcmk_rc_ok;
178 
179  CRM_ASSERT(out != NULL && !pcmk__str_empty(message_id));
180 
181  fn = g_hash_table_lookup(out->messages, message_id);
182  if (fn == NULL) {
183  crm_debug("Called unknown output message '%s' for format '%s'",
184  message_id, out->fmt_name);
185  return EINVAL;
186  }
187 
188  va_start(args, message_id);
189  rc = fn(out, args);
190  va_end(args);
191 
192  return rc;
193 }
194 
195 void
196 pcmk__register_message(pcmk__output_t *out, const char *message_id,
197  pcmk__message_fn_t fn) {
198  CRM_ASSERT(out != NULL && !pcmk__str_empty(message_id) && fn != NULL);
199 
200  g_hash_table_replace(out->messages, pcmk__str_copy(message_id), fn);
201 }
202 
203 void
205 {
206  for (const pcmk__message_entry_t *entry = table; entry->message_id != NULL;
207  entry++) {
208  if (pcmk__strcase_any_of(entry->fmt_name, "default", out->fmt_name, NULL)) {
209  pcmk__register_message(out, entry->message_id, entry->fn);
210  }
211  }
212 }
213 
214 void
216 {
217  if (error == NULL || *error == NULL) {
218  return;
219  }
220 
221  if (out != NULL) {
222  out->err(out, "%s: %s", g_get_prgname(), (*error)->message);
223  } else {
224  fprintf(stderr, "%s: %s\n", g_get_prgname(), (*error)->message);
225  }
226 
227  g_clear_error(error);
228 }
229 
243 int
244 pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml) {
245  pcmk__supported_format_t xml_format[] = {
247  { NULL, NULL, NULL }
248  };
249 
250  if (xml == NULL) {
251  return EINVAL;
252  }
253 
254  if (*xml != NULL) {
255  xmlFreeNode(*xml);
256  *xml = NULL;
257  }
258  pcmk__register_formats(NULL, xml_format);
259  return pcmk__output_new(out, "xml", NULL, NULL);
260 }
261 
270 void
272  xmlNodePtr *xml)
273 {
274  if (out == NULL) {
275  return;
276  }
277 
278  out->finish(out, exit_status, FALSE, (void **) xml);
279  pcmk__output_free(out);
280 }
281 
290 int
292 {
293  int rc = pcmk_rc_ok;
294  const char* argv[] = { "", NULL };
295  pcmk__supported_format_t formats[] = {
297  { NULL, NULL, NULL }
298  };
299 
300  pcmk__register_formats(NULL, formats);
301  rc = pcmk__output_new(out, "log", NULL, (char **) argv);
302  if ((rc != pcmk_rc_ok) || (*out == NULL)) {
303  crm_err("Can't log certain messages due to internal error: %s",
304  pcmk_rc_str(rc));
305  return rc;
306  }
307  return pcmk_rc_ok;
308 }
309 
319 int
320 pcmk__text_output_new(pcmk__output_t **out, const char *filename)
321 {
322  int rc = pcmk_rc_ok;
323  const char* argv[] = { "", NULL };
324  pcmk__supported_format_t formats[] = {
326  { NULL, NULL, NULL }
327  };
328 
329  pcmk__register_formats(NULL, formats);
330  rc = pcmk__output_new(out, "text", filename, (char **) argv);
331  if ((rc != pcmk_rc_ok) || (*out == NULL)) {
332  crm_err("Can't create text output object to internal error: %s",
333  pcmk_rc_str(rc));
334  return rc;
335  }
336  return pcmk_rc_ok;
337 }
int(* pcmk__message_fn_t)(pcmk__output_t *out, va_list args)
#define PCMK__SUPPORTED_FORMAT_LOG
const char * name
Definition: cib.c:26
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1026
int(* message)(pcmk__output_t *out, const char *message_id,...)
#define PCMK__SUPPORTED_FORMAT_XML
void pcmk__unregister_formats(void)
Definition: output.c:166
const char * fmt_name
The name of this output formatter.
#define PCMK__SUPPORTED_FORMAT_TEXT
G_GNUC_INTERNAL void pcmk__register_option_messages(pcmk__output_t *out)
enum crm_exit_e crm_exit_t
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:501
void pcmk__register_formats(GOptionGroup *group, const pcmk__supported_format_t *formats)
Definition: output.c:153
GHashTable * messages
Custom messages that are currently registered on this formatter.
void pcmk__xml_output_finish(pcmk__output_t *out, crm_exit_t exit_status, xmlNodePtr *xml)
Definition: output.c:271
#define crm_debug(fmt, args...)
Definition: logging.h:402
Utility functions.
bool quiet
Should this formatter supress most output?
void pcmk__register_messages(pcmk__output_t *out, const pcmk__message_entry_t *table)
Definition: output.c:204
void(* free_priv)(pcmk__output_t *out)
int(*) int(*) void(* err)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
Wrappers for and extensions to libxml2.
void(* finish)(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest)
int pcmk__output_new(pcmk__output_t **out, const char *fmt_name, const char *filename, char **argv)
Definition: output.c:113
int pcmk__call_message(pcmk__output_t *out, const char *message_id,...)
Definition: output.c:174
#define pcmk__str_copy(str)
int pcmk__bare_output_new(pcmk__output_t **out, const char *fmt_name, const char *filename, char **argv)
Definition: output.c:63
int pcmk__text_output_new(pcmk__output_t **out, const char *filename)
Definition: output.c:320
int pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml)
Definition: output.c:244
pcmk__output_t *(* pcmk__output_factory_t)(char **argv)
void pcmk__register_message(pcmk__output_t *out, const char *message_id, pcmk__message_fn_t fn)
Definition: output.c:196
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:683
void pcmk__output_free(pcmk__output_t *out)
Definition: output.c:30
#define crm_err(fmt, args...)
Definition: logging.h:391
const char * name
The name of this output formatter, which should match the fmt_name parameter in some pcmk__output_t s...
#define CRM_ASSERT(expr)
Definition: results.h:42
gchar * request
A copy of the request that generated this output.
void pcmk__output_and_clear_error(GError **error, pcmk__output_t *out)
Definition: output.c:215
int pcmk__log_output_new(pcmk__output_t **out)
Definition: output.c:291
This structure contains everything that makes up a single output formatter.
const char * message_id
The message to be handled.
int pcmk__register_format(GOptionGroup *group, const char *name, pcmk__output_factory_t create, const GOptionEntry *options)
Definition: output.c:127
G_GNUC_INTERNAL void pcmk__register_patchset_messages(pcmk__output_t *out)