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