This source file includes following definitions.
- html_free_priv
- html_init
- add_error_node
- 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
- html_spacer
- html_progress
- pcmk__mk_html_output
- pcmk__output_create_html_node
- pcmk__html_create
- pcmk__html_add_header
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <ctype.h>
13 #include <stdarg.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16
17 #include <libxml/HTMLtree.h>
18 #include <libxml/tree.h>
19 #include <libxml/xmlstring.h>
20
21 #include <crm/common/cmdline_internal.h>
22 #include <crm/common/xml.h>
23
24 static const char *stylesheet_default =
25 "." PCMK__VALUE_BOLD " { font-weight: bold }\n"
26
27 "." PCMK_VALUE_ONLINE " { color: green }\n"
28 "." PCMK_VALUE_OFFLINE " { color: red }\n"
29 "." PCMK__VALUE_MAINT " { color: blue }\n"
30 "." PCMK_VALUE_STANDBY " { color: blue }\n"
31 "." PCMK__VALUE_HEALTH_RED " { color: red }\n"
32 "." PCMK__VALUE_HEALTH_YELLOW " { color: GoldenRod }\n"
33
34 "." PCMK__VALUE_RSC_FAILED " { color: red }\n"
35 "." PCMK__VALUE_RSC_FAILURE_IGNORED " { color: DarkGreen }\n"
36 "." PCMK__VALUE_RSC_MANAGED " { color: blue }\n"
37 "." PCMK__VALUE_RSC_MULTIPLE " { color: orange }\n"
38 "." PCMK__VALUE_RSC_OK " { color: green }\n"
39
40 "." PCMK__VALUE_WARNING " { color: red; font-weight: bold }";
41
42 static gboolean cgi_output = FALSE;
43 static char *stylesheet_link = NULL;
44 static char *title = NULL;
45 static GSList *extra_headers = NULL;
46
47 GOptionEntry pcmk__html_output_entries[] = {
48 { "html-cgi", 0, 0, G_OPTION_ARG_NONE, &cgi_output,
49 "Add CGI headers (requires --output-as=html)",
50 NULL },
51
52 { "html-stylesheet", 0, 0, G_OPTION_ARG_STRING, &stylesheet_link,
53 "Link to an external stylesheet (requires --output-as=html)",
54 "URI" },
55
56 { "html-title", 0, 0, G_OPTION_ARG_STRING, &title,
57 "Specify a page title (requires --output-as=html)",
58 "TITLE" },
59
60 { NULL }
61 };
62
63
64
65
66
67
68
69 typedef struct private_data_s {
70
71 xmlNode *root;
72 GQueue *parent_q;
73 GSList *errors;
74
75 } private_data_t;
76
77 static void
78 html_free_priv(pcmk__output_t *out) {
79 private_data_t *priv = NULL;
80
81 if (out == NULL || out->priv == NULL) {
82 return;
83 }
84
85 priv = out->priv;
86
87 pcmk__xml_free(priv->root);
88
89
90
91
92 g_queue_free(priv->parent_q);
93 g_slist_free_full(priv->errors, free);
94 free(priv);
95 out->priv = NULL;
96 }
97
98 static bool
99 html_init(pcmk__output_t *out) {
100 private_data_t *priv = NULL;
101
102 pcmk__assert(out != NULL);
103
104
105 if (out->priv != NULL) {
106 return true;
107 } else {
108 out->priv = calloc(1, sizeof(private_data_t));
109 if (out->priv == NULL) {
110 return false;
111 }
112
113 priv = out->priv;
114 }
115
116 priv->parent_q = g_queue_new();
117
118 priv->root = pcmk__xe_create(NULL, "html");
119 xmlCreateIntSubset(priv->root->doc, (const xmlChar *) "html", NULL, NULL);
120
121 crm_xml_add(priv->root, PCMK_XA_LANG, PCMK__VALUE_EN);
122 g_queue_push_tail(priv->parent_q, priv->root);
123 priv->errors = NULL;
124
125 pcmk__output_xml_create_parent(out, "body", NULL);
126
127 return true;
128 }
129
130 static void
131 add_error_node(gpointer data, gpointer user_data) {
132 char *str = (char *) data;
133 pcmk__output_t *out = (pcmk__output_t *) user_data;
134 out->list_item(out, NULL, "%s", str);
135 }
136
137 static void
138 html_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
139 private_data_t *priv = NULL;
140 htmlNodePtr head_node = NULL;
141 htmlNodePtr charset_node = NULL;
142 xmlNode *child_node = NULL;
143
144 pcmk__assert(out != NULL);
145
146 priv = out->priv;
147
148
149
150
151 if (priv == NULL || priv->root == NULL) {
152 return;
153 }
154
155 if (cgi_output && print) {
156 fprintf(out->dest, "Content-Type: text/html\n\n");
157 }
158
159
160
161
162
163 head_node = pcmk__xe_create(priv->root, "head");
164 xmlAddPrevSibling(priv->root->children, head_node);
165
166 if (title != NULL ) {
167 child_node = pcmk__xe_create(head_node, "title");
168 pcmk__xe_set_content(child_node, "%s", title);
169 } else if (out->request != NULL) {
170 child_node = pcmk__xe_create(head_node, "title");
171 pcmk__xe_set_content(child_node, "%s", out->request);
172 }
173
174 charset_node = pcmk__xe_create(head_node, PCMK__XE_META);
175 crm_xml_add(charset_node, "charset", "utf-8");
176
177
178 for (GSList *iter = extra_headers; iter != NULL; iter = iter->next) {
179 pcmk__xml_copy(head_node, (xmlNode *) iter->data);
180 }
181
182
183
184
185
186
187
188 child_node = pcmk__xe_create(head_node, "style");
189 pcmk__xe_set_content(child_node, "%s", stylesheet_default);
190
191 if (stylesheet_link != NULL) {
192 htmlNodePtr link_node = pcmk__xe_create(head_node, "link");
193 pcmk__xe_set_props(link_node, "rel", "stylesheet",
194 "href", stylesheet_link,
195 NULL);
196 }
197
198 if (g_slist_length(priv->errors) > 0) {
199 out->begin_list(out, "Errors", NULL, NULL);
200 g_slist_foreach(priv->errors, add_error_node, (gpointer) out);
201 out->end_list(out);
202 }
203
204 if (print) {
205 htmlDocDump(out->dest, priv->root->doc);
206 }
207
208 if (copy_dest != NULL) {
209 *copy_dest = pcmk__xml_copy(NULL, priv->root);
210 }
211
212 g_slist_free_full(extra_headers, (GDestroyNotify) pcmk__xml_free);
213 extra_headers = NULL;
214 }
215
216 static void
217 html_reset(pcmk__output_t *out) {
218 pcmk__assert(out != NULL);
219
220 out->dest = freopen(NULL, "w", out->dest);
221 pcmk__assert(out->dest != NULL);
222
223 html_free_priv(out);
224 html_init(out);
225 }
226
227 static void
228 html_subprocess_output(pcmk__output_t *out, int exit_status,
229 const char *proc_stdout, const char *proc_stderr) {
230 char *rc_buf = NULL;
231
232 pcmk__assert(out != NULL);
233
234 rc_buf = crm_strdup_printf("Return code: %d", exit_status);
235
236 pcmk__output_create_xml_text_node(out, "h2", "Command Output");
237 pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL, rc_buf);
238
239 if (proc_stdout != NULL) {
240 pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL, "Stdout");
241 pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL,
242 PCMK__VALUE_OUTPUT, proc_stdout);
243 }
244 if (proc_stderr != NULL) {
245 pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL, "Stderr");
246 pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL,
247 PCMK__VALUE_OUTPUT, proc_stderr);
248 }
249
250 free(rc_buf);
251 }
252
253 static void
254 html_version(pcmk__output_t *out, bool extended) {
255 pcmk__assert(out != NULL);
256
257 pcmk__output_create_xml_text_node(out, "h2", "Version Information");
258 pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL,
259 "Program: Pacemaker");
260 pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL,
261 "Version: " PACEMAKER_VERSION);
262 pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL,
263 "Author: Andrew Beekhof and "
264 "the Pacemaker project contributors");
265 pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL,
266 "Build: " BUILD_VERSION);
267 pcmk__output_create_html_node(out, PCMK__XE_DIV, NULL, NULL,
268 "Features: " CRM_FEATURES);
269 }
270
271 G_GNUC_PRINTF(2, 3)
272 static void
273 html_err(pcmk__output_t *out, const char *format, ...) {
274 private_data_t *priv = NULL;
275 int len = 0;
276 char *buf = NULL;
277 va_list ap;
278
279 pcmk__assert((out != NULL) && (out->priv != NULL));
280 priv = out->priv;
281
282 va_start(ap, format);
283 len = vasprintf(&buf, format, ap);
284 pcmk__assert(len >= 0);
285 va_end(ap);
286
287 priv->errors = g_slist_append(priv->errors, buf);
288 }
289
290 G_GNUC_PRINTF(2, 3)
291 static int
292 html_info(pcmk__output_t *out, const char *format, ...) {
293 return pcmk_rc_no_output;
294 }
295
296 static void
297 html_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
298 htmlNodePtr node = NULL;
299
300 pcmk__assert(out != NULL);
301
302 node = pcmk__output_create_html_node(out, "pre", NULL, NULL, buf);
303 crm_xml_add(node, PCMK_XA_LANG, "xml");
304 }
305
306 G_GNUC_PRINTF(4, 5)
307 static void
308 html_begin_list(pcmk__output_t *out, const char *singular_noun,
309 const char *plural_noun, const char *format, ...) {
310 int q_len = 0;
311 private_data_t *priv = NULL;
312 xmlNodePtr node = NULL;
313
314 pcmk__assert((out != NULL) && (out->priv != NULL));
315 priv = out->priv;
316
317
318
319
320
321 q_len = g_queue_get_length(priv->parent_q);
322 if (q_len > 2) {
323 pcmk__output_xml_create_parent(out, "li", NULL);
324 }
325
326 if (format != NULL) {
327 va_list ap;
328 char *buf = NULL;
329 int len;
330
331 va_start(ap, format);
332 len = vasprintf(&buf, format, ap);
333 va_end(ap);
334 pcmk__assert(len >= 0);
335
336 if (q_len > 2) {
337 pcmk__output_create_xml_text_node(out, "h3", buf);
338 } else {
339 pcmk__output_create_xml_text_node(out, "h2", buf);
340 }
341
342 free(buf);
343 }
344
345 node = pcmk__output_xml_create_parent(out, "ul", NULL);
346 g_queue_push_tail(priv->parent_q, node);
347 }
348
349 G_GNUC_PRINTF(3, 4)
350 static void
351 html_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
352 htmlNodePtr item_node = NULL;
353 va_list ap;
354 char *buf = NULL;
355 int len;
356
357 pcmk__assert(out != NULL);
358
359 va_start(ap, format);
360 len = vasprintf(&buf, format, ap);
361 pcmk__assert(len >= 0);
362 va_end(ap);
363
364 item_node = pcmk__output_create_xml_text_node(out, "li", buf);
365 free(buf);
366
367 if (name != NULL) {
368 crm_xml_add(item_node, PCMK_XA_CLASS, name);
369 }
370 }
371
372 static void
373 html_increment_list(pcmk__output_t *out) {
374
375 }
376
377 static void
378 html_end_list(pcmk__output_t *out) {
379 private_data_t *priv = NULL;
380
381 pcmk__assert((out != NULL) && (out->priv != NULL));
382 priv = out->priv;
383
384
385
386
387 g_queue_pop_tail(priv->parent_q);
388 pcmk__output_xml_pop_parent(out);
389
390
391 if (g_queue_get_length(priv->parent_q) > 2) {
392 pcmk__output_xml_pop_parent(out);
393 }
394 }
395
396 static bool
397 html_is_quiet(pcmk__output_t *out) {
398 return false;
399 }
400
401 static void
402 html_spacer(pcmk__output_t *out) {
403 pcmk__assert(out != NULL);
404 pcmk__output_create_xml_node(out, "br", NULL);
405 }
406
407 static void
408 html_progress(pcmk__output_t *out, bool end) {
409
410 }
411
412 pcmk__output_t *
413 pcmk__mk_html_output(char **argv) {
414 pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
415
416 if (retval == NULL) {
417 return NULL;
418 }
419
420 retval->fmt_name = "html";
421 retval->request = pcmk__quote_cmdline(argv);
422
423 retval->init = html_init;
424 retval->free_priv = html_free_priv;
425 retval->finish = html_finish;
426 retval->reset = html_reset;
427
428 retval->register_message = pcmk__register_message;
429 retval->message = pcmk__call_message;
430
431 retval->subprocess_output = html_subprocess_output;
432 retval->version = html_version;
433 retval->info = html_info;
434 retval->transient = html_info;
435 retval->err = html_err;
436 retval->output_xml = html_output_xml;
437
438 retval->begin_list = html_begin_list;
439 retval->list_item = html_list_item;
440 retval->increment_list = html_increment_list;
441 retval->end_list = html_end_list;
442
443 retval->is_quiet = html_is_quiet;
444 retval->spacer = html_spacer;
445 retval->progress = html_progress;
446 retval->prompt = pcmk__text_prompt;
447
448 return retval;
449 }
450
451 xmlNodePtr
452 pcmk__output_create_html_node(pcmk__output_t *out, const char *element_name, const char *id,
453 const char *class_name, const char *text) {
454 htmlNodePtr node = NULL;
455
456 pcmk__assert(out != NULL);
457 CRM_CHECK(pcmk__str_eq(out->fmt_name, "html", pcmk__str_none), return NULL);
458
459 node = pcmk__output_create_xml_text_node(out, element_name, text);
460
461 if (class_name != NULL) {
462 crm_xml_add(node, PCMK_XA_CLASS, class_name);
463 }
464
465 if (id != NULL) {
466 crm_xml_add(node, PCMK_XA_ID, id);
467 }
468
469 return node;
470 }
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485 xmlNode *
486 pcmk__html_create(xmlNode *parent, const char *name, const char *id,
487 const char *class_name)
488 {
489 xmlNode *node = pcmk__xe_create(parent, name);
490
491 pcmk__xe_set_props(node,
492 PCMK_XA_CLASS, class_name,
493 PCMK_XA_ID, id,
494 NULL);
495 return node;
496 }
497
498 void
499 pcmk__html_add_header(const char *name, ...) {
500 htmlNodePtr header_node;
501 va_list ap;
502
503 va_start(ap, name);
504
505 header_node = pcmk__xe_create(NULL, name);
506 while (1) {
507 char *key = va_arg(ap, char *);
508 char *value;
509
510 if (key == NULL) {
511 break;
512 }
513
514 value = va_arg(ap, char *);
515 crm_xml_add(header_node, key, value);
516 }
517
518 extra_headers = g_slist_append(extra_headers, header_node);
519
520 va_end(ap);
521 }