pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
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
18static GHashTable *formatters = NULL;
19
20#if defined(PCMK__UNIT_TESTING)
21// LCOV_EXCL_START
22GHashTable *
23pcmk__output_formatters(void) {
24 return formatters;
25}
26// LCOV_EXCL_STOP
27#endif
28
29void
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
62int
63pcmk__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 pcmk__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) {
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
112int
113pcmk__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
126int
127pcmk__register_format(GOptionGroup *group, const char *name,
129 const GOptionEntry *options)
130{
131 char *name_copy = NULL;
132
133 pcmk__assert((create != NULL) && !pcmk__str_empty(name));
134
135 // cppcheck doesn't understand the above pcmk__assert line
136 // cppcheck-suppress ctunullpointer
137 name_copy = strdup(name);
138 if (name_copy == NULL) {
139 return ENOMEM;
140 }
141
142 if (formatters == NULL) {
143 formatters = pcmk__strkey_table(free, NULL);
144 }
145
146 if (options != NULL && group != NULL) {
147 g_option_group_add_entries(group, options);
148 }
149
150 g_hash_table_insert(formatters, name_copy, create);
151 return pcmk_rc_ok;
152}
153
154void
155pcmk__register_formats(GOptionGroup *group,
156 const pcmk__supported_format_t *formats)
157{
158 if (formats == NULL) {
159 return;
160 }
161 for (const pcmk__supported_format_t *entry = formats; entry->name != NULL;
162 entry++) {
163 pcmk__register_format(group, entry->name, entry->create, entry->options);
164 }
165}
166
167void
169 if (formatters != NULL) {
170 g_hash_table_destroy(formatters);
171 formatters = NULL;
172 }
173}
174
175int
176pcmk__call_message(pcmk__output_t *out, const char *message_id, ...) {
177 va_list args;
178 int rc = pcmk_rc_ok;
180
181 pcmk__assert((out != NULL) && !pcmk__str_empty(message_id));
182
183 fn = g_hash_table_lookup(out->messages, message_id);
184 if (fn == NULL) {
185 crm_debug("Called unknown output message '%s' for format '%s'",
186 message_id, out->fmt_name);
187 return EINVAL;
188 }
189
190 va_start(args, message_id);
191 rc = fn(out, args);
192 va_end(args);
193
194 return rc;
195}
196
197void
198pcmk__register_message(pcmk__output_t *out, const char *message_id,
200{
201 pcmk__assert((out != NULL) && !pcmk__str_empty(message_id) && (fn != NULL));
202 g_hash_table_replace(out->messages, pcmk__str_copy(message_id), fn);
203}
204
205void
207{
208 for (const pcmk__message_entry_t *entry = table; entry->message_id != NULL;
209 entry++) {
210 if (pcmk__strcase_any_of(entry->fmt_name, "default", out->fmt_name, NULL)) {
211 pcmk__register_message(out, entry->message_id, entry->fn);
212 }
213 }
214}
215
216void
218{
219 if (error == NULL || *error == NULL) {
220 return;
221 }
222
223 if (out != NULL) {
224 out->err(out, "%s: %s", g_get_prgname(), (*error)->message);
225 } else {
226 fprintf(stderr, "%s: %s\n", g_get_prgname(), (*error)->message);
227 }
228
229 g_clear_error(error);
230}
231
245int
246pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml) {
247 pcmk__supported_format_t xml_format[] = {
249 { NULL, NULL, NULL }
250 };
251
252 if (xml == NULL) {
253 return EINVAL;
254 }
255
256 if (*xml != NULL) {
257 pcmk__xml_free(*xml);
258 *xml = NULL;
259 }
260 pcmk__register_formats(NULL, xml_format);
261 return pcmk__output_new(out, "xml", NULL, NULL);
262}
263
272void
274 xmlNodePtr *xml)
275{
276 if (out == NULL) {
277 return;
278 }
279
280 out->finish(out, exit_status, FALSE, (void **) xml);
282}
283
292int
294{
295 int rc = pcmk_rc_ok;
296 const char* argv[] = { "", NULL };
297 pcmk__supported_format_t formats[] = {
299 { NULL, NULL, NULL }
300 };
301
302 pcmk__register_formats(NULL, formats);
303 rc = pcmk__output_new(out, "log", NULL, (char **) argv);
304 if ((rc != pcmk_rc_ok) || (*out == NULL)) {
305 crm_err("Can't log certain messages due to internal error: %s",
306 pcmk_rc_str(rc));
307 return rc;
308 }
309 return pcmk_rc_ok;
310}
311
321int
322pcmk__text_output_new(pcmk__output_t **out, const char *filename)
323{
324 int rc = pcmk_rc_ok;
325 const char* argv[] = { "", NULL };
326 pcmk__supported_format_t formats[] = {
328 { NULL, NULL, NULL }
329 };
330
331 pcmk__register_formats(NULL, formats);
332 rc = pcmk__output_new(out, "text", filename, (char **) argv);
333 if ((rc != pcmk_rc_ok) || (*out == NULL)) {
334 crm_err("Can't create text output object to internal error: %s",
335 pcmk_rc_str(rc));
336 return rc;
337 }
338 return pcmk_rc_ok;
339}
const char * name
Definition cib.c:26
Utility functions.
G_GNUC_INTERNAL void pcmk__register_option_messages(pcmk__output_t *out)
G_GNUC_INTERNAL void pcmk__register_patchset_messages(pcmk__output_t *out)
#define crm_debug(fmt, args...)
Definition logging.h:368
#define crm_err(fmt, args...)
Definition logging.h:357
void pcmk__xml_output_finish(pcmk__output_t *out, crm_exit_t exit_status, xmlNodePtr *xml)
Definition output.c:273
void pcmk__register_messages(pcmk__output_t *out, const pcmk__message_entry_t *table)
Definition output.c:206
int pcmk__register_format(GOptionGroup *group, const char *name, pcmk__output_factory_t create, const GOptionEntry *options)
Definition output.c:127
void pcmk__output_free(pcmk__output_t *out)
Definition output.c:30
int pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml)
Definition output.c:246
void pcmk__register_formats(GOptionGroup *group, const pcmk__supported_format_t *formats)
Definition output.c:155
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:176
void pcmk__output_and_clear_error(GError **error, pcmk__output_t *out)
Definition output.c:217
void pcmk__register_message(pcmk__output_t *out, const char *message_id, pcmk__message_fn_t fn)
Definition output.c:198
int pcmk__log_output_new(pcmk__output_t **out)
Definition output.c:293
int pcmk__text_output_new(pcmk__output_t **out, const char *filename)
Definition output.c:322
int pcmk__bare_output_new(pcmk__output_t **out, const char *fmt_name, const char *filename, char **argv)
Definition output.c:63
void pcmk__unregister_formats(void)
Definition output.c:168
int(* pcmk__message_fn_t)(pcmk__output_t *out, va_list args)
#define PCMK__SUPPORTED_FORMAT_XML
#define PCMK__SUPPORTED_FORMAT_TEXT
#define PCMK__SUPPORTED_FORMAT_LOG
pcmk__output_t *(* pcmk__output_factory_t)(char **argv)
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:617
@ pcmk_rc_ok
Definition results.h:159
@ pcmk_rc_unknown_format
Definition results.h:152
enum crm_exit_e crm_exit_t
Exit status codes for tools and daemons.
#define pcmk__assert(expr)
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:685
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1029
@ pcmk__str_null_matches
#define pcmk__str_copy(str)
const char * message_id
The message to be handled.
This structure contains everything that makes up a single output formatter.
int(* message)(pcmk__output_t *out, const char *message_id,...)
GHashTable * messages
Custom messages that are currently registered on this formatter.
const char * fmt_name
The name of this output formatter.
int int void(* err)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
void(* finish)(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest)
void(* free_priv)(pcmk__output_t *out)
gchar * request
A copy of the request that generated this output.
const char * name
The name of this output formatter, which should match the fmt_name parameter in some pcmk__output_t s...
Wrappers for and extensions to libxml2.
void pcmk__xml_free(xmlNode *xml)
Definition xml.c:816