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_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 ".bold { font-weight: bold }\n"
23
24 ".online { color: green }\n"
25 ".offline { color: red }\n"
26 ".maint { color: blue }\n"
27 ".standby { color: blue }\n"
28 ".health_red { color: red }\n"
29 ".health_yellow { color: GoldenRod }\n"
30
31 ".rsc-failed { color: red }\n"
32 ".rsc-failure-ignored { color: DarkGreen }\n"
33 ".rsc-managed { color: blue }\n"
34 ".rsc-multiple { color: orange }\n"
35 ".rsc-ok { color: green }\n"
36
37 ".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 xmlFreeNode(priv->root);
85 g_queue_free(priv->parent_q);
86 g_slist_free(priv->errors);
87 free(priv);
88 out->priv = NULL;
89 }
90
91 static bool
92 html_init(pcmk__output_t *out) {
93 private_data_t *priv = NULL;
94
95 CRM_ASSERT(out != NULL);
96
97
98 if (out->priv != NULL) {
99 return true;
100 } else {
101 out->priv = calloc(1, sizeof(private_data_t));
102 if (out->priv == NULL) {
103 return false;
104 }
105
106 priv = out->priv;
107 }
108
109 priv->parent_q = g_queue_new();
110
111 priv->root = create_xml_node(NULL, "html");
112 xmlCreateIntSubset(priv->root->doc, (pcmkXmlStr) "html", NULL, NULL);
113
114 crm_xml_add(priv->root, "lang", "en");
115 g_queue_push_tail(priv->parent_q, priv->root);
116 priv->errors = NULL;
117
118 pcmk__output_xml_create_parent(out, "body", NULL);
119
120 return true;
121 }
122
123 static void
124 add_error_node(gpointer data, gpointer user_data) {
125 char *str = (char *) data;
126 pcmk__output_t *out = (pcmk__output_t *) user_data;
127 out->list_item(out, NULL, "%s", str);
128 }
129
130 static void
131 html_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
132 private_data_t *priv = NULL;
133 htmlNodePtr head_node = NULL;
134 htmlNodePtr charset_node = NULL;
135
136 CRM_ASSERT(out != NULL);
137
138 priv = out->priv;
139
140
141
142
143 if (priv == NULL || priv->root == NULL) {
144 return;
145 }
146
147 if (cgi_output && print) {
148 fprintf(out->dest, "Content-Type: text/html\n\n");
149 }
150
151
152
153
154
155 head_node = xmlNewNode(NULL, (pcmkXmlStr) "head");
156
157 if (title != NULL ) {
158 pcmk_create_xml_text_node(head_node, "title", title);
159 } else if (out->request != NULL) {
160 pcmk_create_xml_text_node(head_node, "title", out->request);
161 }
162
163 charset_node = create_xml_node(head_node, "meta");
164 crm_xml_add(charset_node, "charset", "utf-8");
165
166
167 for (int i = 0; i < g_slist_length(extra_headers); i++) {
168 xmlAddChild(head_node, xmlCopyNode(g_slist_nth_data(extra_headers, i), 1));
169 }
170
171
172
173
174
175
176
177 pcmk_create_xml_text_node(head_node, "style", stylesheet_default);
178
179 if (stylesheet_link != NULL) {
180 htmlNodePtr link_node = create_xml_node(head_node, "link");
181 pcmk__xe_set_props(link_node, "rel", "stylesheet",
182 "href", stylesheet_link,
183 NULL);
184 }
185
186 xmlAddPrevSibling(priv->root->children, head_node);
187
188 if (g_slist_length(priv->errors) > 0) {
189 out->begin_list(out, "Errors", NULL, NULL);
190 g_slist_foreach(priv->errors, add_error_node, (gpointer) out);
191 out->end_list(out);
192 }
193
194 if (print) {
195 htmlDocDump(out->dest, priv->root->doc);
196 }
197
198 if (copy_dest != NULL) {
199 *copy_dest = copy_xml(priv->root);
200 }
201
202 g_slist_free_full(extra_headers, (GDestroyNotify) xmlFreeNode);
203 extra_headers = NULL;
204 }
205
206 static void
207 html_reset(pcmk__output_t *out) {
208 CRM_ASSERT(out != NULL);
209
210 out->dest = freopen(NULL, "w", out->dest);
211 CRM_ASSERT(out->dest != NULL);
212
213 html_free_priv(out);
214 html_init(out);
215 }
216
217 static void
218 html_subprocess_output(pcmk__output_t *out, int exit_status,
219 const char *proc_stdout, const char *proc_stderr) {
220 char *rc_buf = NULL;
221
222 CRM_ASSERT(out != NULL);
223
224 rc_buf = crm_strdup_printf("Return code: %d", exit_status);
225
226 pcmk__output_create_xml_text_node(out, "h2", "Command Output");
227 pcmk__output_create_html_node(out, "div", NULL, NULL, rc_buf);
228
229 if (proc_stdout != NULL) {
230 pcmk__output_create_html_node(out, "div", NULL, NULL, "Stdout");
231 pcmk__output_create_html_node(out, "div", NULL, "output", proc_stdout);
232 }
233 if (proc_stderr != NULL) {
234 pcmk__output_create_html_node(out, "div", NULL, NULL, "Stderr");
235 pcmk__output_create_html_node(out, "div", NULL, "output", proc_stderr);
236 }
237
238 free(rc_buf);
239 }
240
241 static void
242 html_version(pcmk__output_t *out, bool extended) {
243 CRM_ASSERT(out != NULL);
244
245 pcmk__output_create_xml_text_node(out, "h2", "Version Information");
246 pcmk__output_create_html_node(out, "div", NULL, NULL, "Program: Pacemaker");
247 pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Version: %s", PACEMAKER_VERSION));
248 pcmk__output_create_html_node(out, "div", NULL, NULL,
249 "Author: Andrew Beekhof and "
250 "the Pacemaker project contributors");
251 pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Build: %s", BUILD_VERSION));
252 pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Features: %s", CRM_FEATURES));
253 }
254
255 G_GNUC_PRINTF(2, 3)
256 static void
257 html_err(pcmk__output_t *out, const char *format, ...) {
258 private_data_t *priv = NULL;
259 int len = 0;
260 char *buf = NULL;
261 va_list ap;
262
263 CRM_ASSERT(out != NULL && out->priv != NULL);
264 priv = out->priv;
265
266 va_start(ap, format);
267 len = vasprintf(&buf, format, ap);
268 CRM_ASSERT(len >= 0);
269 va_end(ap);
270
271 priv->errors = g_slist_append(priv->errors, buf);
272 }
273
274 G_GNUC_PRINTF(2, 3)
275 static int
276 html_info(pcmk__output_t *out, const char *format, ...) {
277 return pcmk_rc_no_output;
278 }
279
280 static void
281 html_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
282 htmlNodePtr node = NULL;
283
284 CRM_ASSERT(out != NULL);
285
286 node = pcmk__output_create_html_node(out, "pre", NULL, NULL, buf);
287 crm_xml_add(node, "lang", "xml");
288 }
289
290 G_GNUC_PRINTF(4, 5)
291 static void
292 html_begin_list(pcmk__output_t *out, const char *singular_noun,
293 const char *plural_noun, const char *format, ...) {
294 int q_len = 0;
295 private_data_t *priv = NULL;
296 xmlNodePtr node = NULL;
297
298 CRM_ASSERT(out != NULL && out->priv != NULL);
299 priv = out->priv;
300
301
302
303
304
305 q_len = g_queue_get_length(priv->parent_q);
306 if (q_len > 2) {
307 pcmk__output_xml_create_parent(out, "li", NULL);
308 }
309
310 if (format != NULL) {
311 va_list ap;
312 char *buf = NULL;
313 int len;
314
315 va_start(ap, format);
316 len = vasprintf(&buf, format, ap);
317 va_end(ap);
318 CRM_ASSERT(len >= 0);
319
320 if (q_len > 2) {
321 pcmk__output_create_xml_text_node(out, "h3", buf);
322 } else {
323 pcmk__output_create_xml_text_node(out, "h2", buf);
324 }
325
326 free(buf);
327 }
328
329 node = pcmk__output_xml_create_parent(out, "ul", NULL);
330 g_queue_push_tail(priv->parent_q, node);
331 }
332
333 G_GNUC_PRINTF(3, 4)
334 static void
335 html_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
336 htmlNodePtr item_node = NULL;
337 va_list ap;
338 char *buf = NULL;
339 int len;
340
341 CRM_ASSERT(out != NULL);
342
343 va_start(ap, format);
344 len = vasprintf(&buf, format, ap);
345 CRM_ASSERT(len >= 0);
346 va_end(ap);
347
348 item_node = pcmk__output_create_xml_text_node(out, "li", buf);
349 free(buf);
350
351 if (name != NULL) {
352 crm_xml_add(item_node, "class", name);
353 }
354 }
355
356 static void
357 html_increment_list(pcmk__output_t *out) {
358
359 }
360
361 static void
362 html_end_list(pcmk__output_t *out) {
363 private_data_t *priv = NULL;
364
365 CRM_ASSERT(out != NULL && out->priv != NULL);
366 priv = out->priv;
367
368
369 g_queue_pop_tail(priv->parent_q);
370 pcmk__output_xml_pop_parent(out);
371
372
373 if (g_queue_get_length(priv->parent_q) > 2) {
374 pcmk__output_xml_pop_parent(out);
375 }
376 }
377
378 static bool
379 html_is_quiet(pcmk__output_t *out) {
380 return false;
381 }
382
383 static void
384 html_spacer(pcmk__output_t *out) {
385 CRM_ASSERT(out != NULL);
386 pcmk__output_create_xml_node(out, "br", NULL);
387 }
388
389 static void
390 html_progress(pcmk__output_t *out, bool end) {
391
392 }
393
394 pcmk__output_t *
395 pcmk__mk_html_output(char **argv) {
396 pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
397
398 if (retval == NULL) {
399 return NULL;
400 }
401
402 retval->fmt_name = "html";
403 retval->request = pcmk__quote_cmdline(argv);
404
405 retval->init = html_init;
406 retval->free_priv = html_free_priv;
407 retval->finish = html_finish;
408 retval->reset = html_reset;
409
410 retval->register_message = pcmk__register_message;
411 retval->message = pcmk__call_message;
412
413 retval->subprocess_output = html_subprocess_output;
414 retval->version = html_version;
415 retval->info = html_info;
416 retval->err = html_err;
417 retval->output_xml = html_output_xml;
418
419 retval->begin_list = html_begin_list;
420 retval->list_item = html_list_item;
421 retval->increment_list = html_increment_list;
422 retval->end_list = html_end_list;
423
424 retval->is_quiet = html_is_quiet;
425 retval->spacer = html_spacer;
426 retval->progress = html_progress;
427 retval->prompt = pcmk__text_prompt;
428
429 return retval;
430 }
431
432 xmlNodePtr
433 pcmk__output_create_html_node(pcmk__output_t *out, const char *element_name, const char *id,
434 const char *class_name, const char *text) {
435 htmlNodePtr node = NULL;
436
437 CRM_ASSERT(out != NULL);
438 CRM_CHECK(pcmk__str_eq(out->fmt_name, "html", pcmk__str_none), return NULL);
439
440 node = pcmk__output_create_xml_text_node(out, element_name, text);
441
442 if (class_name != NULL) {
443 crm_xml_add(node, "class", class_name);
444 }
445
446 if (id != NULL) {
447 crm_xml_add(node, "id", id);
448 }
449
450 return node;
451 }
452
453 void
454 pcmk__html_add_header(const char *name, ...) {
455 htmlNodePtr header_node;
456 va_list ap;
457
458 va_start(ap, name);
459
460 header_node = xmlNewNode(NULL, (pcmkXmlStr) name);
461 while (1) {
462 char *key = va_arg(ap, char *);
463 char *value;
464
465 if (key == NULL) {
466 break;
467 }
468
469 value = va_arg(ap, char *);
470 crm_xml_add(header_node, key, value);
471 }
472
473 extra_headers = g_slist_append(extra_headers, header_node);
474
475 va_end(ap);
476 }