1 /*
2  * Copyright 2019-2021 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 void
pcmk__output_free(pcmk__output_t * out)19 pcmk__output_free(pcmk__output_t *out) {
20     out->free_priv(out);
21 
22     if (out->messages != NULL) {
23         g_hash_table_destroy(out->messages);
24     }
25 
26     g_free(out->request);
27     free(out);
28 }
29 
30 int
pcmk__output_new(pcmk__output_t ** out,const char * fmt_name,const char * filename,char ** argv)31 pcmk__output_new(pcmk__output_t **out, const char *fmt_name, const char *filename,
32                  char **argv) {
33     pcmk__output_factory_t create = NULL;;
34 
35     if (formatters == NULL) {
36         return EINVAL;
37     }
38 
39     /* If no name was given, just try "text".  It's up to each tool to register
40      * what it supports so this also may not be valid.
41      */
42     if (fmt_name == NULL) {
43         create = g_hash_table_lookup(formatters, "text");
44     } else {
45         create = g_hash_table_lookup(formatters, fmt_name);
46     }
47 
48     if (create == NULL) {
49         return pcmk_rc_unknown_format;
50     }
51 
52     *out = create(argv);
53     if (*out == NULL) {
54         return ENOMEM;
55     }
56 
57     if (pcmk__str_eq(filename, "-", pcmk__str_null_matches)) {
58         (*out)->dest = stdout;
59     } else {
60         (*out)->dest = fopen(filename, "w");
61         if ((*out)->dest == NULL) {
62             return errno;
63         }
64     }
65 
66     (*out)->quiet = false;
67     (*out)->messages = pcmk__strkey_table(free, NULL);
68 
69     if ((*out)->init(*out) == false) {
70         pcmk__output_free(*out);
71         return ENOMEM;
72     }
73 
74     setenv("OCF_OUTPUT_FORMAT", (*out)->fmt_name, 1);
75 
76     return pcmk_rc_ok;
77 }
78 
79 int
pcmk__register_format(GOptionGroup * group,const char * name,pcmk__output_factory_t create,GOptionEntry * options)80 pcmk__register_format(GOptionGroup *group, const char *name,
81                       pcmk__output_factory_t create, GOptionEntry *options) {
82     if (create == NULL) {
83         return -EINVAL;
84     }
85 
86     if (formatters == NULL) {
87         formatters = pcmk__strkey_table(free, NULL);
88     }
89 
90     if (options != NULL && group != NULL) {
91         g_option_group_add_entries(group, options);
92     }
93 
94     g_hash_table_insert(formatters, strdup(name), create);
95     return 0;
96 }
97 
98 void
pcmk__register_formats(GOptionGroup * group,pcmk__supported_format_t * formats)99 pcmk__register_formats(GOptionGroup *group, pcmk__supported_format_t *formats) {
100     pcmk__supported_format_t *entry = NULL;
101 
102     if (formats == NULL) {
103         return;
104     }
105 
106     for (entry = formats; entry->name != NULL; entry++) {
107         pcmk__register_format(group, entry->name, entry->create, entry->options);
108     }
109 }
110 
111 void
pcmk__unregister_formats()112 pcmk__unregister_formats() {
113     if (formatters != NULL) {
114         g_hash_table_destroy(formatters);
115     }
116 }
117 
118 int
pcmk__call_message(pcmk__output_t * out,const char * message_id,...)119 pcmk__call_message(pcmk__output_t *out, const char *message_id, ...) {
120     va_list args;
121     int rc = pcmk_rc_ok;
122     pcmk__message_fn_t fn;
123 
124     fn = g_hash_table_lookup(out->messages, message_id);
125     if (fn == NULL) {
126         crm_debug("Called unknown output message '%s' for format '%s'",
127                   message_id, out->fmt_name);
128         return EINVAL;
129     }
130 
131     va_start(args, message_id);
132     rc = fn(out, args);
133     va_end(args);
134 
135     return rc;
136 }
137 
138 void
pcmk__register_message(pcmk__output_t * out,const char * message_id,pcmk__message_fn_t fn)139 pcmk__register_message(pcmk__output_t *out, const char *message_id,
140                        pcmk__message_fn_t fn) {
141     g_hash_table_replace(out->messages, strdup(message_id), fn);
142 }
143 
144 void
pcmk__register_messages(pcmk__output_t * out,pcmk__message_entry_t * table)145 pcmk__register_messages(pcmk__output_t *out, pcmk__message_entry_t *table) {
146     pcmk__message_entry_t *entry;
147 
148     for (entry = table; entry->message_id != NULL; entry++) {
149         if (pcmk__strcase_any_of(entry->fmt_name, "default", out->fmt_name, NULL)) {
150             pcmk__register_message(out, entry->message_id, entry->fn);
151         }
152     }
153 }
154 
155 void
pcmk__output_and_clear_error(GError * error,pcmk__output_t * out)156 pcmk__output_and_clear_error(GError *error, pcmk__output_t *out)
157 {
158     if (error == NULL) {
159         return;
160     }
161 
162     if (out != NULL) {
163         out->err(out, "%s: %s", g_get_prgname(), error->message);
164     } else {
165         fprintf(stderr, "%s: %s\n", g_get_prgname(), error->message);
166     }
167 
168     g_clear_error(&error);
169 }
170