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