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