pacemaker 2.1.5-a3f44794f94
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
16static GHashTable *formatters = NULL;
17
18#if defined(PCMK__UNIT_TESTING)
19GHashTable *
20pcmk__output_formatters(void) {
21 return formatters;
22}
23#endif
24
25void
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
41int
42pcmk__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) {
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) {
82 return ENOMEM;
83 }
84
85 setenv("OCF_OUTPUT_FORMAT", (*out)->fmt_name, 1);
86
87 return pcmk_rc_ok;
88}
89
90int
91pcmk__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
109void
110pcmk__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
122void
124 if (formatters != NULL) {
125 g_hash_table_destroy(formatters);
126 formatters = NULL;
127 }
128}
129
130int
131pcmk__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
152void
153pcmk__register_message(pcmk__output_t *out, const char *message_id,
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
160void
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
171void
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
200int
201pcmk__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
222void
224 out->finish(out, 0, FALSE, (void **) xml);
226}
227
236int
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}
const char * name
Definition: cib.c:24
Utility functions.
#define crm_debug(fmt, args...)
Definition: logging.h:364
#define crm_err(fmt, args...)
Definition: logging.h:359
void pcmk__register_messages(pcmk__output_t *out, const pcmk__message_entry_t *table)
Definition: output.c:161
int pcmk__register_format(GOptionGroup *group, const char *name, pcmk__output_factory_t create, const GOptionEntry *options)
Definition: output.c:91
void pcmk__output_free(pcmk__output_t *out)
Definition: output.c:26
int pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml)
Definition: output.c:201
void pcmk__register_formats(GOptionGroup *group, const pcmk__supported_format_t *formats)
Definition: output.c:110
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__register_message(pcmk__output_t *out, const char *message_id, pcmk__message_fn_t fn)
Definition: output.c:153
int pcmk__log_output_new(pcmk__output_t **out)
Definition: output.c:237
void pcmk__xml_output_finish(pcmk__output_t *out, xmlNodePtr *xml)
Definition: output.c:223
void pcmk__output_and_clear_error(GError *error, pcmk__output_t *out)
Definition: output.c:172
void pcmk__unregister_formats()
Definition: output.c:123
int(* pcmk__message_fn_t)(pcmk__output_t *out, va_list args)
#define PCMK__SUPPORTED_FORMAT_XML
#define PCMK__SUPPORTED_FORMAT_LOG
pcmk__output_t *(* pcmk__output_factory_t)(char **argv)
int setenv(const char *name, const char *value, int why)
#define CRM_ASSERT(expr)
Definition: results.h:42
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:476
@ pcmk_rc_ok
Definition: results.h:148
@ pcmk_rc_unknown_format
Definition: results.h:142
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:611
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:928
@ pcmk__str_null_matches
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(*) 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)
bool quiet
Should this formatter supress most output?
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.