This source file includes following definitions.
- html_free_priv
- html_init
- add_error_node
- finish_reset_common
- html_finish
- html_reset
- html_subprocess_output
- html_version
- G_GNUC_PRINTF
- G_GNUC_PRINTF
- html_output_xml
- G_GNUC_PRINTF
- G_GNUC_PRINTF
- html_increment_list
- html_end_list
- html_is_quiet
- pcmk__mk_html_output
- pcmk__output_create_html_node
- pcmk__html_add_header
1
2
3
4
5
6
7
8
9
10 #ifndef _GNU_SOURCE
11 # define _GNU_SOURCE
12 #endif
13
14 #include <ctype.h>
15 #include <libxml/HTMLtree.h>
16 #include <stdarg.h>
17 #include <stdlib.h>
18 #include <stdio.h>
19
20 #include <crm/crm.h>
21 #include <crm/common/output_internal.h>
22 #include <crm/common/xml.h>
23
24 static const char *stylesheet_default =
25 ".bold { font-weight: bold }\n"
26 ".maint { color: blue }\n"
27 ".offline { color: red }\n"
28 ".online { color: green }\n"
29 ".rsc-failed { color: red }\n"
30 ".rsc-failure-ignored { color: yellow }\n"
31 ".rsc-managed { color: yellow }\n"
32 ".rsc-multiple { color: orange }\n"
33 ".rsc-ok { color: green }\n"
34 ".standby { color: orange }\n"
35 ".warning { color: red, font-weight: bold }";
36
37 static gboolean cgi_output = FALSE;
38 static char *stylesheet_link = NULL;
39 static char *title = NULL;
40 static GSList *extra_headers = NULL;
41
42 GOptionEntry pcmk__html_output_entries[] = {
43 { "html-cgi", 0, 0, G_OPTION_ARG_NONE, &cgi_output,
44 "Add CGI headers (requires --output-as=html)",
45 NULL },
46
47 { "html-stylesheet", 0, 0, G_OPTION_ARG_STRING, &stylesheet_link,
48 "Link to an external stylesheet (requires --output-as=html)",
49 "URI" },
50
51 { "html-title", 0, 0, G_OPTION_ARG_STRING, &title,
52 "Specify a page title (requires --output-as=html)",
53 "TITLE" },
54
55 { NULL }
56 };
57
58 typedef struct private_data_s {
59 xmlNode *root;
60 GQueue *parent_q;
61 GSList *errors;
62 } private_data_t;
63
64 static void
65 html_free_priv(pcmk__output_t *out) {
66 private_data_t *priv = out->priv;
67
68 if (priv == NULL) {
69 return;
70 }
71
72 xmlFreeNode(priv->root);
73 g_queue_free(priv->parent_q);
74 g_slist_free(priv->errors);
75 free(priv);
76 out->priv = NULL;
77 }
78
79 static bool
80 html_init(pcmk__output_t *out) {
81 private_data_t *priv = NULL;
82
83
84 if (out->priv != NULL) {
85 return true;
86 } else {
87 out->priv = calloc(1, sizeof(private_data_t));
88 if (out->priv == NULL) {
89 return false;
90 }
91
92 priv = out->priv;
93 }
94
95 priv->parent_q = g_queue_new();
96
97 priv->root = create_xml_node(NULL, "html");
98 xmlCreateIntSubset(priv->root->doc, (pcmkXmlStr) "html", NULL, NULL);
99
100 xmlSetProp(priv->root, (pcmkXmlStr) "lang", (pcmkXmlStr) "en");
101 g_queue_push_tail(priv->parent_q, priv->root);
102 priv->errors = NULL;
103
104 pcmk__output_xml_create_parent(out, "body");
105
106 return true;
107 }
108
109 static void
110 add_error_node(gpointer data, gpointer user_data) {
111 char *str = (char *) data;
112 pcmk__output_t *out = (pcmk__output_t *) user_data;
113 out->list_item(out, NULL, "%s", str);
114 }
115
116 static void
117 finish_reset_common(pcmk__output_t *out, crm_exit_t exit_status, bool print) {
118 private_data_t *priv = out->priv;
119 htmlNodePtr head_node = NULL;
120 htmlNodePtr charset_node = NULL;
121
122 if (cgi_output && print) {
123 fprintf(out->dest, "Content-Type: text/html\n\n");
124 }
125
126
127
128
129
130 head_node = xmlNewNode(NULL, (pcmkXmlStr) "head");
131
132 if (title != NULL ) {
133 pcmk_create_xml_text_node(head_node, "title", title);
134 } else if (out->request != NULL) {
135 pcmk_create_xml_text_node(head_node, "title", out->request);
136 }
137
138 charset_node = create_xml_node(head_node, "meta");
139 xmlSetProp(charset_node, (pcmkXmlStr) "charset", (pcmkXmlStr) "utf-8");
140
141
142 for (int i = 0; i < g_slist_length(extra_headers); i++) {
143 xmlAddChild(head_node, xmlCopyNode(g_slist_nth_data(extra_headers, i), 1));
144 }
145
146
147
148
149
150
151
152 pcmk_create_xml_text_node(head_node, "style", stylesheet_default);
153
154 if (stylesheet_link != NULL) {
155 htmlNodePtr link_node = create_xml_node(head_node, "link");
156 xmlSetProp(link_node, (pcmkXmlStr) "rel", (pcmkXmlStr) "stylesheet");
157 xmlSetProp(link_node, (pcmkXmlStr) "href", (pcmkXmlStr) stylesheet_link);
158 }
159
160 xmlAddPrevSibling(priv->root->children, head_node);
161
162 if (g_slist_length(priv->errors) > 0) {
163 out->begin_list(out, "Errors", NULL, NULL);
164 g_slist_foreach(priv->errors, add_error_node, (gpointer) out);
165 out->end_list(out);
166 }
167
168 if (print) {
169 htmlDocDump(out->dest, priv->root->doc);
170 }
171 }
172
173 static void
174 html_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
175 private_data_t *priv = out->priv;
176
177
178
179
180 if (priv == NULL || priv->root == NULL) {
181 return;
182 }
183
184 finish_reset_common(out, exit_status, print);
185
186 if (copy_dest != NULL) {
187 *copy_dest = copy_xml(priv->root);
188 }
189
190 g_slist_free_full(extra_headers, (GDestroyNotify) xmlFreeNode);
191 }
192
193 static void
194 html_reset(pcmk__output_t *out) {
195 CRM_ASSERT(out != NULL);
196
197 out->dest = freopen(NULL, "w", out->dest);
198 CRM_ASSERT(out->dest != NULL);
199
200 if (out->priv != NULL) {
201 finish_reset_common(out, CRM_EX_OK, true);
202 }
203
204 html_free_priv(out);
205 html_init(out);
206 }
207
208 static void
209 html_subprocess_output(pcmk__output_t *out, int exit_status,
210 const char *proc_stdout, const char *proc_stderr) {
211 char *rc_buf = NULL;
212 private_data_t *priv = out->priv;
213 CRM_ASSERT(priv != NULL);
214
215 rc_buf = crm_strdup_printf("Return code: %d", exit_status);
216
217 pcmk__output_create_xml_text_node(out, "h2", "Command Output");
218 pcmk__output_create_html_node(out, "div", NULL, NULL, rc_buf);
219
220 if (proc_stdout != NULL) {
221 pcmk__output_create_html_node(out, "div", NULL, NULL, "Stdout");
222 pcmk__output_create_html_node(out, "div", NULL, "output", proc_stdout);
223 }
224 if (proc_stderr != NULL) {
225 pcmk__output_create_html_node(out, "div", NULL, NULL, "Stderr");
226 pcmk__output_create_html_node(out, "div", NULL, "output", proc_stderr);
227 }
228
229 free(rc_buf);
230 }
231
232 static void
233 html_version(pcmk__output_t *out, bool extended) {
234 private_data_t *priv = out->priv;
235 CRM_ASSERT(priv != NULL);
236
237 pcmk__output_create_xml_text_node(out, "h2", "Version Information");
238 pcmk__output_create_html_node(out, "div", NULL, NULL, "Program: Pacemaker");
239 pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Version: %s", PACEMAKER_VERSION));
240 pcmk__output_create_html_node(out, "div", NULL, NULL, "Author: Andrew Beekhof");
241 pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Build: %s", BUILD_VERSION));
242 pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Features: %s", CRM_FEATURES));
243 }
244
245 G_GNUC_PRINTF(2, 3)
246 static void
247 html_err(pcmk__output_t *out, const char *format, ...) {
248 private_data_t *priv = out->priv;
249 int len = 0;
250 char *buf = NULL;
251 va_list ap;
252
253 CRM_ASSERT(priv != NULL);
254 va_start(ap, format);
255 len = vasprintf(&buf, format, ap);
256 CRM_ASSERT(len >= 0);
257 va_end(ap);
258
259 priv->errors = g_slist_append(priv->errors, buf);
260 }
261
262 G_GNUC_PRINTF(2, 3)
263 static void
264 html_info(pcmk__output_t *out, const char *format, ...) {
265
266 }
267
268 static void
269 html_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
270 htmlNodePtr node = NULL;
271 private_data_t *priv = out->priv;
272
273 CRM_ASSERT(priv != NULL);
274
275 node = pcmk__output_create_html_node(out, "pre", NULL, NULL, buf);
276 xmlSetProp(node, (pcmkXmlStr) "lang", (pcmkXmlStr) "xml");
277 }
278
279 G_GNUC_PRINTF(4, 5)
280 static void
281 html_begin_list(pcmk__output_t *out, const char *singular_noun,
282 const char *plural_noun, const char *format, ...) {
283 int q_len = 0;
284 private_data_t *priv = out->priv;
285 xmlNodePtr node = NULL;
286
287 CRM_ASSERT(priv != NULL);
288
289
290
291
292
293 q_len = g_queue_get_length(priv->parent_q);
294 if (q_len > 2) {
295 pcmk__output_xml_create_parent(out, "li");
296 }
297
298 if (format != NULL) {
299 va_list ap;
300 char *buf = NULL;
301 int len;
302
303 va_start(ap, format);
304 len = vasprintf(&buf, format, ap);
305 va_end(ap);
306 CRM_ASSERT(len >= 0);
307
308 if (q_len > 2) {
309 pcmk__output_create_xml_text_node(out, "h3", buf);
310 } else {
311 pcmk__output_create_xml_text_node(out, "h2", buf);
312 }
313
314 free(buf);
315 }
316
317 node = pcmk__output_xml_create_parent(out, "ul");
318 g_queue_push_tail(priv->parent_q, node);
319 }
320
321 G_GNUC_PRINTF(3, 4)
322 static void
323 html_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
324 private_data_t *priv = out->priv;
325 htmlNodePtr item_node = NULL;
326 va_list ap;
327 char *buf = NULL;
328 int len;
329
330 CRM_ASSERT(priv != NULL);
331
332 va_start(ap, format);
333 len = vasprintf(&buf, format, ap);
334 CRM_ASSERT(len >= 0);
335 va_end(ap);
336
337 item_node = pcmk__output_create_xml_text_node(out, "li", buf);
338 free(buf);
339
340 if (name != NULL) {
341 xmlSetProp(item_node, (pcmkXmlStr) "class", (pcmkXmlStr) name);
342 }
343 }
344
345 static void
346 html_increment_list(pcmk__output_t *out) {
347
348 }
349
350 static void
351 html_end_list(pcmk__output_t *out) {
352 private_data_t *priv = out->priv;
353
354 CRM_ASSERT(priv != NULL);
355
356
357 g_queue_pop_tail(priv->parent_q);
358 pcmk__output_xml_pop_parent(out);
359
360
361 if (g_queue_get_length(priv->parent_q) > 2) {
362 pcmk__output_xml_pop_parent(out);
363 }
364 }
365
366 static bool
367 html_is_quiet(pcmk__output_t *out) {
368 return false;
369 }
370
371 pcmk__output_t *
372 pcmk__mk_html_output(char **argv) {
373 pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
374
375 if (retval == NULL) {
376 return NULL;
377 }
378
379 retval->fmt_name = "html";
380 retval->request = argv == NULL ? NULL : g_strjoinv(" ", argv);
381
382 retval->init = html_init;
383 retval->free_priv = html_free_priv;
384 retval->finish = html_finish;
385 retval->reset = html_reset;
386
387 retval->register_message = pcmk__register_message;
388 retval->message = pcmk__call_message;
389
390 retval->subprocess_output = html_subprocess_output;
391 retval->version = html_version;
392 retval->info = html_info;
393 retval->err = html_err;
394 retval->output_xml = html_output_xml;
395
396 retval->begin_list = html_begin_list;
397 retval->list_item = html_list_item;
398 retval->increment_list = html_increment_list;
399 retval->end_list = html_end_list;
400
401 retval->is_quiet = html_is_quiet;
402
403 return retval;
404 }
405
406 xmlNodePtr
407 pcmk__output_create_html_node(pcmk__output_t *out, const char *element_name, const char *id,
408 const char *class_name, const char *text) {
409 htmlNodePtr node = pcmk__output_create_xml_text_node(out, element_name, text);
410
411 if (class_name != NULL) {
412 xmlSetProp(node, (pcmkXmlStr) "class", (pcmkXmlStr) class_name);
413 }
414
415 if (id != NULL) {
416 xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) id);
417 }
418
419 return node;
420 }
421
422 void
423 pcmk__html_add_header(const char *name, ...) {
424 htmlNodePtr header_node;
425 va_list ap;
426
427 va_start(ap, name);
428
429 header_node = xmlNewNode(NULL, (pcmkXmlStr) name);
430 while (1) {
431 char *key = va_arg(ap, char *);
432 char *value;
433
434 if (key == NULL) {
435 break;
436 }
437
438 value = va_arg(ap, char *);
439 xmlSetProp(header_node, (pcmkXmlStr) key, (pcmkXmlStr) value);
440 }
441
442 extra_headers = g_slist_append(extra_headers, header_node);
443
444 va_end(ap);
445 }