pacemaker  2.1.5-b7adf64e51
Scalable High-Availability cluster resource manager
output.c
Go to the documentation of this file.
1 /*
2  * Copyright 2019-2022 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 static GHashTable *formatters = NULL;
17 
18 #if defined(PCMK__UNIT_TESTING)
19 GHashTable *
20 pcmk__output_formatters(void) {
21  return formatters;
22 }
23 #endif
24 
25 void
27  if (out == NULL) {
28  return;
29  }
30 
31  out->free_priv(out);
32 
33  if (out->messages != NULL) {
34  g_hash_table_destroy(out->messages);
35  }
36 
37  g_free(out->request);
38  free(out);
39 }
40 
41 int
42 pcmk__output_new(pcmk__output_t **out, const char *fmt_name, const char *filename,
43  char **argv) {
44  pcmk__output_factory_t create = NULL;
45 
46  CRM_ASSERT(formatters != NULL && out != NULL);
47 
48  /* If no name was given, just try "text". It's up to each tool to register
49  * what it supports so this also may not be valid.
50  */
51  if (fmt_name == NULL) {
52  create = g_hash_table_lookup(formatters, "text");
53  } else {
54  create = g_hash_table_lookup(formatters, fmt_name);
55  }
56 
57  if (create == NULL) {
59  }
60 
61  *out = create(argv);
62  if (*out == NULL) {
63  return ENOMEM;
64  }
65 
66  if (pcmk__str_eq(filename, "-", pcmk__str_null_matches)) {
67  (*out)->dest = stdout;
68  } else {
69  (*out)->dest = fopen(filename, "w");
70  if ((*out)->dest == NULL) {
71  pcmk__output_free(*out);
72  *out = NULL;
73  return errno;
74  }
75  }
76 
77  (*out)->quiet = false;
78  (*out)->messages = pcmk__strkey_table(free, NULL);
79 
80  if ((*out)->init(*out) == false) {
81  pcmk__output_free(*out);
82  return ENOMEM;
83  }
84 
85  setenv("OCF_OUTPUT_FORMAT", (*out)->fmt_name, 1);
86 
87  return pcmk_rc_ok;
88 }
89 
90 int
91 pcmk__register_format(GOptionGroup *group, const char *name,
93  const GOptionEntry *options)
94 {
95  CRM_ASSERT(create != NULL && !pcmk__str_empty(name));
96 
97  if (formatters == NULL) {
98  formatters = pcmk__strkey_table(free, NULL);
99  }
100 
101  if (options != NULL && group != NULL) {
102  g_option_group_add_entries(group, options);
103  }
104 
105  g_hash_table_insert(formatters, strdup(name), create);
106  return pcmk_rc_ok;
107 }
108 
109 void
110 pcmk__register_formats(GOptionGroup *group,
111  const pcmk__supported_format_t *formats)
112 {
113  if (formats == NULL) {
114  return;
115  }
116  for (const pcmk__supported_format_t *entry = formats; entry->name != NULL;
117  entry++) {
118  pcmk__register_format(group, entry->name, entry->create, entry->options);
119  }
120 }
121 
122 void
124  if (formatters != NULL) {
125  g_hash_table_destroy(formatters);
126  formatters = NULL;
127  }
128 }
129 
130 int
131 pcmk__call_message(pcmk__output_t *out, const char *message_id, ...) {
132  va_list args;
133  int rc = pcmk_rc_ok;
135 
136  CRM_ASSERT(out != NULL && !pcmk__str_empty(message_id));
137 
138  fn = g_hash_table_lookup(out->messages, message_id);
139  if (fn == NULL) {
140  crm_debug("Called unknown output message '%s' for format '%s'",
141  message_id, out->fmt_name);
142  return EINVAL;
143  }
144 
145  va_start(args, message_id);
146  rc = fn(out, args);
147  va_end(args);
148 
149  return rc;
150 }
151 
152 void
153 pcmk__register_message(pcmk__output_t *out, const char *message_id,
154  pcmk__message_fn_t fn) {
155  CRM_ASSERT(out != NULL && !pcmk__str_empty(message_id) && fn != NULL);
156 
157  g_hash_table_replace(out->messages, strdup(message_id), fn);
158 }
159 
160 void
162 {
163  for (const pcmk__message_entry_t *entry = table; entry->message_id != NULL;
164  entry++) {
165  if (pcmk__strcase_any_of(entry->fmt_name, "default", out->fmt_name, NULL)) {
166  pcmk__register_message(out, entry->message_id, entry->fn);
167  }
168  }
169 }
170 
171 void
173 {
174  if (error == NULL) {
175  return;
176  }
177 
178  if (out != NULL) {
179  out->err(out, "%s: %s", g_get_prgname(), error->message);
180  } else {
181  fprintf(stderr, "%s: %s\n", g_get_prgname(), error->message);
182  }
183 
184  g_clear_error(&error);
185 }
186 
200 int
201 pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml) {
202  pcmk__supported_format_t xml_format[] = {
204  { NULL, NULL, NULL }
205  };
206 
207  if (*xml != NULL) {
208  xmlFreeNode(*xml);
209  *xml = NULL;
210  }
211  pcmk__register_formats(NULL, xml_format);
212  return pcmk__output_new(out, "xml", NULL, NULL);
213 }
214 
222 void
223 pcmk__xml_output_finish(pcmk__output_t *out, xmlNodePtr *xml) {
224  out->finish(out, 0, FALSE, (void **) xml);
225  pcmk__output_free(out);
226 }
227 
236 int
238 {
239  int rc = pcmk_rc_ok;
240  const char* argv[] = { "", NULL };
241  pcmk__supported_format_t formats[] = {
243  { NULL, NULL, NULL }
244  };
245 
246  pcmk__register_formats(NULL, formats);
247  rc = pcmk__output_new(out, "log", NULL, (char **) argv);
248  if ((rc != pcmk_rc_ok) || (*out == NULL)) {
249  crm_err("Can't log certain messages due to internal error: %s",
250  pcmk_rc_str(rc));
251  return rc;
252  }
253  return pcmk_rc_ok;
254 }
int(* pcmk__message_fn_t)(pcmk__output_t *out, va_list args)
void pcmk__output_and_clear_error(GError *error, pcmk__output_t *out)
Definition: output.c:172
#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:928
int(* message)(pcmk__output_t *out, const char *message_id,...)
#define PCMK__SUPPORTED_FORMAT_XML
const char * fmt_name
The name of this output formatter.
void pcmk__xml_output_finish(pcmk__output_t *out, xmlNodePtr *xml)
Definition: output.c:223
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:476
void pcmk__register_formats(GOptionGroup *group, const pcmk__supported_format_t *formats)
Definition: output.c:110
GHashTable * messages
Custom messages that are currently registered on this formatter.
#define crm_debug(fmt, args...)
Definition: logging.h:364
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:161
void(* free_priv)(pcmk__output_t *out)
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:42
int pcmk__call_message(pcmk__output_t *out, const char *message_id,...)
Definition: output.c:131
void pcmk__unregister_formats()
Definition: output.c:123
int pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml)
Definition: output.c:201
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:153
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:26
#define crm_err(fmt, args...)
Definition: logging.h:359
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.
int pcmk__log_output_new(pcmk__output_t **out)
Definition: output.c:237
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:91