pacemaker  2.1.6-802a72226b
Scalable High-Availability cluster resource manager
output.c
Go to the documentation of this file.
1 /*
2  * Copyright 2019-2023 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 GHashTable *
22 pcmk__output_formatters(void) {
23  return formatters;
24 }
25 #endif
26 
27 void
29  if (out == NULL) {
30  return;
31  }
32 
33  out->free_priv(out);
34 
35  if (out->messages != NULL) {
36  g_hash_table_destroy(out->messages);
37  }
38 
39  g_free(out->request);
40  free(out);
41 }
42 
60 int
61 pcmk__bare_output_new(pcmk__output_t **out, const char *fmt_name,
62  const char *filename, char **argv)
63 {
64  pcmk__output_factory_t create = NULL;
65 
66  CRM_ASSERT(formatters != NULL && out != NULL);
67 
68  /* If no name was given, just try "text". It's up to each tool to register
69  * what it supports so this also may not be valid.
70  */
71  if (fmt_name == NULL) {
72  create = g_hash_table_lookup(formatters, "text");
73  } else {
74  create = g_hash_table_lookup(formatters, fmt_name);
75  }
76 
77  if (create == NULL) {
79  }
80 
81  *out = create(argv);
82  if (*out == NULL) {
83  return ENOMEM;
84  }
85 
86  if (pcmk__str_eq(filename, "-", pcmk__str_null_matches)) {
87  (*out)->dest = stdout;
88  } else {
89  (*out)->dest = fopen(filename, "w");
90  if ((*out)->dest == NULL) {
91  pcmk__output_free(*out);
92  *out = NULL;
93  return errno;
94  }
95  }
96 
97  (*out)->quiet = false;
98  (*out)->messages = pcmk__strkey_table(free, NULL);
99 
100  if ((*out)->init(*out) == false) {
101  pcmk__output_free(*out);
102  return ENOMEM;
103  }
104 
105  setenv("OCF_OUTPUT_FORMAT", (*out)->fmt_name, 1);
106 
107  return pcmk_rc_ok;
108 }
109 
110 int
111 pcmk__output_new(pcmk__output_t **out, const char *fmt_name,
112  const char *filename, char **argv)
113 {
114  int rc = pcmk__bare_output_new(out, fmt_name, filename, argv);
115 
116  if (rc == pcmk_rc_ok) {
117  /* Register libcrmcommon messages (currently they exist only for
118  * patchset)
119  */
121  }
122  return rc;
123 }
124 
125 int
126 pcmk__register_format(GOptionGroup *group, const char *name,
127  pcmk__output_factory_t create,
128  const GOptionEntry *options)
129 {
130  CRM_ASSERT(create != NULL && !pcmk__str_empty(name));
131 
132  if (formatters == NULL) {
133  formatters = pcmk__strkey_table(free, NULL);
134  }
135 
136  if (options != NULL && group != NULL) {
137  g_option_group_add_entries(group, options);
138  }
139 
140  g_hash_table_insert(formatters, strdup(name), create);
141  return pcmk_rc_ok;
142 }
143 
144 void
145 pcmk__register_formats(GOptionGroup *group,
146  const pcmk__supported_format_t *formats)
147 {
148  if (formats == NULL) {
149  return;
150  }
151  for (const pcmk__supported_format_t *entry = formats; entry->name != NULL;
152  entry++) {
153  pcmk__register_format(group, entry->name, entry->create, entry->options);
154  }
155 }
156 
157 void
159  if (formatters != NULL) {
160  g_hash_table_destroy(formatters);
161  formatters = NULL;
162  }
163 }
164 
165 int
166 pcmk__call_message(pcmk__output_t *out, const char *message_id, ...) {
167  va_list args;
168  int rc = pcmk_rc_ok;
170 
171  CRM_ASSERT(out != NULL && !pcmk__str_empty(message_id));
172 
173  fn = g_hash_table_lookup(out->messages, message_id);
174  if (fn == NULL) {
175  crm_debug("Called unknown output message '%s' for format '%s'",
176  message_id, out->fmt_name);
177  return EINVAL;
178  }
179 
180  va_start(args, message_id);
181  rc = fn(out, args);
182  va_end(args);
183 
184  return rc;
185 }
186 
187 void
188 pcmk__register_message(pcmk__output_t *out, const char *message_id,
189  pcmk__message_fn_t fn) {
190  CRM_ASSERT(out != NULL && !pcmk__str_empty(message_id) && fn != NULL);
191 
192  g_hash_table_replace(out->messages, strdup(message_id), fn);
193 }
194 
195 void
197 {
198  for (const pcmk__message_entry_t *entry = table; entry->message_id != NULL;
199  entry++) {
200  if (pcmk__strcase_any_of(entry->fmt_name, "default", out->fmt_name, NULL)) {
201  pcmk__register_message(out, entry->message_id, entry->fn);
202  }
203  }
204 }
205 
206 void
208 {
209  if (error == NULL || *error == NULL) {
210  return;
211  }
212 
213  if (out != NULL) {
214  out->err(out, "%s: %s", g_get_prgname(), (*error)->message);
215  } else {
216  fprintf(stderr, "%s: %s\n", g_get_prgname(), (*error)->message);
217  }
218 
219  g_clear_error(error);
220 }
221 
235 int
236 pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml) {
237  pcmk__supported_format_t xml_format[] = {
239  { NULL, NULL, NULL }
240  };
241 
242  if (*xml != NULL) {
243  xmlFreeNode(*xml);
244  *xml = NULL;
245  }
246  pcmk__register_formats(NULL, xml_format);
247  return pcmk__output_new(out, "xml", NULL, NULL);
248 }
249 
257 void
258 pcmk__xml_output_finish(pcmk__output_t *out, xmlNodePtr *xml) {
259  out->finish(out, 0, FALSE, (void **) xml);
260  pcmk__output_free(out);
261 }
262 
271 int
273 {
274  int rc = pcmk_rc_ok;
275  const char* argv[] = { "", NULL };
276  pcmk__supported_format_t formats[] = {
278  { NULL, NULL, NULL }
279  };
280 
281  pcmk__register_formats(NULL, formats);
282  rc = pcmk__output_new(out, "log", NULL, (char **) argv);
283  if ((rc != pcmk_rc_ok) || (*out == NULL)) {
284  crm_err("Can't log certain messages due to internal error: %s",
285  pcmk_rc_str(rc));
286  return rc;
287  }
288  return pcmk_rc_ok;
289 }
290 
300 int
301 pcmk__text_output_new(pcmk__output_t **out, const char *filename)
302 {
303  int rc = pcmk_rc_ok;
304  const char* argv[] = { "", NULL };
305  pcmk__supported_format_t formats[] = {
307  { NULL, NULL, NULL }
308  };
309 
310  pcmk__register_formats(NULL, formats);
311  rc = pcmk__output_new(out, "text", filename, (char **) argv);
312  if ((rc != pcmk_rc_ok) || (*out == NULL)) {
313  crm_err("Can't create text output object to internal error: %s",
314  pcmk_rc_str(rc));
315  return rc;
316  }
317  return pcmk_rc_ok;
318 }
int(* pcmk__message_fn_t)(pcmk__output_t *out, va_list args)
#define PCMK__SUPPORTED_FORMAT_LOG
const char * name
Definition: cib.c:24
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:933
int(* message)(pcmk__output_t *out, const char *message_id,...)
#define PCMK__SUPPORTED_FORMAT_XML
void pcmk__unregister_formats(void)
Definition: output.c:158
const char * fmt_name
The name of this output formatter.
#define PCMK__SUPPORTED_FORMAT_TEXT
void pcmk__xml_output_finish(pcmk__output_t *out, xmlNodePtr *xml)
Definition: output.c:258
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:488
void pcmk__register_formats(GOptionGroup *group, const pcmk__supported_format_t *formats)
Definition: output.c:145
GHashTable * messages
Custom messages that are currently registered on this formatter.
#define crm_debug(fmt, args...)
Definition: logging.h:382
Utility functions.
bool quiet
Should this formatter supress most output?
int setenv(const char *name, const char *value, int why)
void pcmk__register_messages(pcmk__output_t *out, const pcmk__message_entry_t *table)
Definition: output.c:196
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:111
int pcmk__call_message(pcmk__output_t *out, const char *message_id,...)
Definition: output.c:166
int pcmk__bare_output_new(pcmk__output_t **out, const char *fmt_name, const char *filename, char **argv)
Definition: output.c:61
int pcmk__text_output_new(pcmk__output_t **out, const char *filename)
Definition: output.c:301
int pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml)
Definition: output.c:236
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:188
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:611
void pcmk__output_free(pcmk__output_t *out)
Definition: output.c:28
#define crm_err(fmt, args...)
Definition: logging.h:377
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:207
int pcmk__log_output_new(pcmk__output_t **out)
Definition: output.c:272
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:126
G_GNUC_INTERNAL void pcmk__register_patchset_messages(pcmk__output_t *out)