1 /*
2  * Copyright 2019-2021 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 #include <stdarg.h>
12 
13 #include <crm/stonith-ng.h>
14 #include <crm/common/iso8601.h>
15 #include <crm/common/util.h>
16 #include <crm/common/xml.h>
17 #include <crm/common/output.h>
18 #include <crm/common/output_internal.h>
19 #include <crm/common/xml_internal.h>
20 #include <crm/fencing/internal.h>
21 #include <crm/pengine/internal.h>
22 
23 static char *
time_t_string(time_t when)24 time_t_string(time_t when) {
25     crm_time_t *crm_when = crm_time_new(NULL);
26     char *buf = NULL;
27 
28     crm_time_set_timet(crm_when, &when);
29     buf = crm_time_as_string(crm_when, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
30     crm_time_free(crm_when);
31     return buf;
32 }
33 
34 PCMK__OUTPUT_ARGS("failed-fencing-list", "stonith_history_t *", "GList *",
35                   "unsigned int", "gboolean")
36 int
stonith__failed_history(pcmk__output_t * out,va_list args)37 stonith__failed_history(pcmk__output_t *out, va_list args) {
38     stonith_history_t *history = va_arg(args, stonith_history_t *);
39     GList *only_node = va_arg(args, GList *);
40     unsigned int section_opts = va_arg(args, unsigned int);
41     gboolean print_spacer = va_arg(args, gboolean);
42 
43     int rc = pcmk_rc_no_output;
44 
45     for (stonith_history_t *hp = history; hp; hp = hp->next) {
46         if (hp->state != st_failed) {
47             continue;
48         }
49 
50         if (!pcmk__str_in_list(only_node, hp->target, pcmk__str_casei)) {
51             continue;
52         }
53 
54         PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Failed Fencing Actions");
55         out->message(out, "stonith-event", hp, pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
56                      stonith__later_succeeded(hp, history));
57         out->increment_list(out);
58     }
59 
60     PCMK__OUTPUT_LIST_FOOTER(out, rc);
61     return rc;
62 }
63 
64 PCMK__OUTPUT_ARGS("fencing-list", "stonith_history_t *", "GList *", "unsigned int", "gboolean")
65 int
stonith__history(pcmk__output_t * out,va_list args)66 stonith__history(pcmk__output_t *out, va_list args) {
67     stonith_history_t *history = va_arg(args, stonith_history_t *);
68     GList *only_node = va_arg(args, GList *);
69     unsigned int section_opts = va_arg(args, unsigned int);
70     gboolean print_spacer = va_arg(args, gboolean);
71 
72     int rc = pcmk_rc_no_output;
73 
74     for (stonith_history_t *hp = history; hp; hp = hp->next) {
75         if (!pcmk__str_in_list(only_node, hp->target, pcmk__str_casei)) {
76             continue;
77         }
78 
79         if (hp->state != st_failed) {
80             PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Fencing History");
81             out->message(out, "stonith-event", hp, pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
82                          stonith__later_succeeded(hp, history));
83             out->increment_list(out);
84         }
85     }
86 
87     PCMK__OUTPUT_LIST_FOOTER(out, rc);
88     return rc;
89 }
90 
91 PCMK__OUTPUT_ARGS("full-fencing-list", "crm_exit_t", "stonith_history_t *", "GList *",
92                   "unsigned int", "gboolean")
93 int
stonith__full_history(pcmk__output_t * out,va_list args)94 stonith__full_history(pcmk__output_t *out, va_list args) {
95     crm_exit_t history_rc G_GNUC_UNUSED = va_arg(args, crm_exit_t);
96     stonith_history_t *history = va_arg(args, stonith_history_t *);
97     GList *only_node = va_arg(args, GList *);
98     unsigned int section_opts = va_arg(args, unsigned int);
99     gboolean print_spacer = va_arg(args, gboolean);
100 
101     int rc = pcmk_rc_no_output;
102 
103     for (stonith_history_t *hp = history; hp; hp = hp->next) {
104         if (!pcmk__str_in_list(only_node, hp->target, pcmk__str_casei)) {
105             continue;
106         }
107 
108         PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Fencing History");
109         out->message(out, "stonith-event", hp, pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
110                      stonith__later_succeeded(hp, history));
111         out->increment_list(out);
112     }
113 
114     PCMK__OUTPUT_LIST_FOOTER(out, rc);
115     return rc;
116 }
117 
118 PCMK__OUTPUT_ARGS("full-fencing-list", "crm_exit_t", "stonith_history_t *", "GList *",
119                   "unsigned int", "gboolean")
120 static int
full_history_xml(pcmk__output_t * out,va_list args)121 full_history_xml(pcmk__output_t *out, va_list args) {
122     crm_exit_t history_rc = va_arg(args, crm_exit_t);
123     stonith_history_t *history = va_arg(args, stonith_history_t *);
124     GList *only_node = va_arg(args, GList *);
125     unsigned int section_opts = va_arg(args, unsigned int);
126     gboolean print_spacer G_GNUC_UNUSED = va_arg(args, gboolean);
127 
128     int rc = pcmk_rc_no_output;
129 
130     if (history_rc == 0) {
131         for (stonith_history_t *hp = history; hp; hp = hp->next) {
132             if (!pcmk__str_in_list(only_node, hp->target, pcmk__str_casei)) {
133                 continue;
134             }
135 
136             PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Fencing History");
137             out->message(out, "stonith-event", hp, pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
138                          stonith__later_succeeded(hp, history));
139             out->increment_list(out);
140         }
141 
142         PCMK__OUTPUT_LIST_FOOTER(out, rc);
143     } else {
144         char *rc_s = pcmk__itoa(history_rc);
145 
146         pcmk__output_create_xml_node(out, "fence_history",
147                                      "status", rc_s,
148                                      NULL);
149         free(rc_s);
150 
151         rc = pcmk_rc_ok;
152     }
153 
154     return rc;
155 }
156 
157 PCMK__OUTPUT_ARGS("last-fenced", "const char *", "time_t")
158 static int
last_fenced_html(pcmk__output_t * out,va_list args)159 last_fenced_html(pcmk__output_t *out, va_list args) {
160     const char *target = va_arg(args, const char *);
161     time_t when = va_arg(args, time_t);
162 
163     if (when) {
164         char *buf = crm_strdup_printf("Node %s last fenced at: %s", target, ctime(&when));
165         pcmk__output_create_html_node(out, "div", NULL, NULL, buf);
166         free(buf);
167         return pcmk_rc_ok;
168     } else {
169         return pcmk_rc_no_output;
170     }
171 }
172 
173 PCMK__OUTPUT_ARGS("last-fenced", "const char *", "time_t")
174 static int
last_fenced_text(pcmk__output_t * out,va_list args)175 last_fenced_text(pcmk__output_t *out, va_list args) {
176     const char *target = va_arg(args, const char *);
177     time_t when = va_arg(args, time_t);
178 
179     if (when) {
180         pcmk__indented_printf(out, "Node %s last fenced at: %s", target, ctime(&when));
181     } else {
182         pcmk__indented_printf(out, "Node %s has never been fenced\n", target);
183     }
184 
185     return pcmk_rc_ok;
186 }
187 
188 PCMK__OUTPUT_ARGS("last-fenced", "const char *", "time_t")
189 static int
last_fenced_xml(pcmk__output_t * out,va_list args)190 last_fenced_xml(pcmk__output_t *out, va_list args) {
191     const char *target = va_arg(args, const char *);
192     time_t when = va_arg(args, time_t);
193 
194     if (when) {
195         char *buf = time_t_string(when);
196 
197         pcmk__output_create_xml_node(out, "last-fenced",
198                                      "target", target,
199                                      "when", buf,
200                                      NULL);
201 
202         free(buf);
203         return pcmk_rc_ok;
204     } else {
205         return pcmk_rc_no_output;
206     }
207 }
208 
209 PCMK__OUTPUT_ARGS("pending-fencing-list", "stonith_history_t *", "GList *",
210                   "unsigned int", "gboolean")
211 int
stonith__pending_actions(pcmk__output_t * out,va_list args)212 stonith__pending_actions(pcmk__output_t *out, va_list args) {
213     stonith_history_t *history = va_arg(args, stonith_history_t *);
214     GList *only_node = va_arg(args, GList *);
215     unsigned int section_opts = va_arg(args, unsigned int);
216     gboolean print_spacer = va_arg(args, gboolean);
217 
218     int rc = pcmk_rc_no_output;
219 
220     for (stonith_history_t *hp = history; hp; hp = hp->next) {
221         if (!pcmk__str_in_list(only_node, hp->target, pcmk__str_casei)) {
222             continue;
223         }
224 
225         /* Skip the rest of the history after we see a failed/done action */
226         if ((hp->state == st_failed) || (hp->state == st_done)) {
227             break;
228         }
229 
230         PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Pending Fencing Actions");
231         out->message(out, "stonith-event", hp, pcmk_all_flags_set(section_opts, pcmk_section_fencing_all),
232                      stonith__later_succeeded(hp, history));
233         out->increment_list(out);
234     }
235 
236     PCMK__OUTPUT_LIST_FOOTER(out, rc);
237     return rc;
238 }
239 
240 PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "gboolean", "gboolean")
241 static int
stonith_event_html(pcmk__output_t * out,va_list args)242 stonith_event_html(pcmk__output_t *out, va_list args) {
243     stonith_history_t *event = va_arg(args, stonith_history_t *);
244     gboolean full_history = va_arg(args, gboolean);
245     gboolean later_succeeded = va_arg(args, gboolean);
246 
247     switch(event->state) {
248         case st_done: {
249             char *completed_s = time_t_string(event->completed);
250 
251             out->list_item(out, "successful-stonith-event",
252                            "%s of %s successful: delegate=%s, client=%s, origin=%s, %s='%s'",
253                            stonith_action_str(event->action), event->target,
254                            event->delegate ? event->delegate : "",
255                            event->client, event->origin,
256                            full_history ? "completed" : "last-successful",
257                            completed_s);
258             free(completed_s);
259             break;
260         }
261 
262         case st_failed: {
263             char *failed_s = time_t_string(event->completed);
264 
265             out->list_item(out, "failed-stonith-event",
266                            "%s of %s failed : delegate=%s, client=%s, origin=%s, %s='%s' %s",
267                            stonith_action_str(event->action), event->target,
268                            event->delegate ? event->delegate : "",
269                            event->client, event->origin,
270                            full_history ? "completed" : "last-failed",
271                            failed_s,
272                            later_succeeded ? "(a later attempt succeeded)" : "");
273             free(failed_s);
274             break;
275         }
276 
277         default:
278             out->list_item(out, "pending-stonith-event",
279                            "%s of %s pending: client=%s, origin=%s",
280                            stonith_action_str(event->action), event->target,
281                            event->client, event->origin);
282             break;
283     }
284 
285     return pcmk_rc_ok;
286 }
287 
288 PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "gboolean", "gboolean")
289 static int
stonith_event_text(pcmk__output_t * out,va_list args)290 stonith_event_text(pcmk__output_t *out, va_list args) {
291     stonith_history_t *event = va_arg(args, stonith_history_t *);
292     gboolean full_history = va_arg(args, gboolean);
293     gboolean later_succeeded = va_arg(args, gboolean);
294 
295     char *buf = time_t_string(event->completed);
296 
297     switch (event->state) {
298         case st_failed:
299             pcmk__indented_printf(out, "%s of %s failed: delegate=%s, client=%s, origin=%s, %s='%s' %s\n",
300                                   stonith_action_str(event->action), event->target,
301                                   event->delegate ? event->delegate : "",
302                                   event->client, event->origin,
303                                   full_history ? "completed" : "last-failed", buf,
304                                   later_succeeded ? "(a later attempt succeeded)" : "");
305             break;
306 
307         case st_done:
308             pcmk__indented_printf(out, "%s of %s successful: delegate=%s, client=%s, origin=%s, %s='%s'\n",
309                                   stonith_action_str(event->action), event->target,
310                                   event->delegate ? event->delegate : "",
311                                   event->client, event->origin,
312                                   full_history ? "completed" : "last-successful", buf);
313             break;
314 
315         default:
316             pcmk__indented_printf(out, "%s of %s pending: client=%s, origin=%s\n",
317                                   stonith_action_str(event->action), event->target,
318                                   event->client, event->origin);
319             break;
320     }
321 
322     free(buf);
323     return pcmk_rc_ok;
324 }
325 
326 PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "gboolean", "gboolean")
327 static int
stonith_event_xml(pcmk__output_t * out,va_list args)328 stonith_event_xml(pcmk__output_t *out, va_list args) {
329     stonith_history_t *event = va_arg(args, stonith_history_t *);
330     gboolean full_history G_GNUC_UNUSED = va_arg(args, gboolean);
331     gboolean later_succeeded G_GNUC_UNUSED = va_arg(args, gboolean);
332 
333     char *buf = NULL;
334 
335     xmlNodePtr node = pcmk__output_create_xml_node(out, "fence_event",
336                                                    "action", event->action,
337                                                    "target", event->target,
338                                                    "client", event->client,
339                                                    "origin", event->origin,
340                                                    NULL);
341 
342     switch (event->state) {
343         case st_failed:
344             crm_xml_add(node, "status", "failed");
345             break;
346 
347         case st_done:
348             crm_xml_add(node, "status", "success");
349             break;
350 
351         default: {
352             char *state = pcmk__itoa(event->state);
353             pcmk__xe_set_props(node, "status", "pending",
354                                "extended-status", state,
355                                NULL);
356             free(state);
357             break;
358         }
359     }
360 
361     if (event->delegate != NULL) {
362         crm_xml_add(node, "delegate", event->delegate);
363     }
364 
365     if (event->state == st_failed || event->state == st_done) {
366         buf = time_t_string(event->completed);
367         crm_xml_add(node, "completed", buf);
368         free(buf);
369     }
370 
371     return pcmk_rc_ok;
372 }
373 
374 PCMK__OUTPUT_ARGS("validate", "const char *", "const char *", "char *", "char *", "int")
375 static int
validate_agent_html(pcmk__output_t * out,va_list args)376 validate_agent_html(pcmk__output_t *out, va_list args) {
377     const char *agent = va_arg(args, const char *);
378     const char *device = va_arg(args, const char *);
379     char *output = va_arg(args, char *);
380     char *error_output = va_arg(args, char *);
381     int rc = va_arg(args, int);
382 
383     if (device) {
384         char *buf = crm_strdup_printf("Validation of %s on %s %s", agent, device,
385                                       rc ? "failed" : "succeeded");
386         pcmk__output_create_html_node(out, "div", NULL, NULL, buf);
387         free(buf);
388     } else {
389         char *buf = crm_strdup_printf("Validation of %s %s", agent,
390                                       rc ? "failed" : "succeeded");
391         pcmk__output_create_html_node(out, "div", NULL, NULL, buf);
392         free(buf);
393     }
394 
395     out->subprocess_output(out, rc, output, error_output);
396     return rc;
397 }
398 
399 PCMK__OUTPUT_ARGS("validate", "const char *", "const char *", "char *", "char *", "int")
400 static int
validate_agent_text(pcmk__output_t * out,va_list args)401 validate_agent_text(pcmk__output_t *out, va_list args) {
402     const char *agent = va_arg(args, const char *);
403     const char *device = va_arg(args, const char *);
404     char *output = va_arg(args, char *);
405     char *error_output = va_arg(args, char *);
406     int rc = va_arg(args, int);
407 
408     if (device) {
409         pcmk__indented_printf(out, "Validation of %s on %s %s\n", agent, device,
410                               rc ? "failed" : "succeeded");
411     } else {
412         pcmk__indented_printf(out, "Validation of %s %s\n", agent,
413                               rc ? "failed" : "succeeded");
414     }
415 
416     out->subprocess_output(out, rc, output, error_output);
417     return rc;
418 }
419 
420 PCMK__OUTPUT_ARGS("validate", "const char *", "const char *", "char *", "char *", "int")
421 static int
validate_agent_xml(pcmk__output_t * out,va_list args)422 validate_agent_xml(pcmk__output_t *out, va_list args) {
423     const char *agent = va_arg(args, const char *);
424     const char *device = va_arg(args, const char *);
425     char *output = va_arg(args, char *);
426     char *error_output = va_arg(args, char *);
427     int rc = va_arg(args, int);
428 
429     xmlNodePtr node = pcmk__output_create_xml_node(out, "validate",
430                                                    "agent", agent,
431                                                    "valid", pcmk__btoa(rc),
432                                                    NULL);
433 
434     if (device != NULL) {
435         crm_xml_add(node, "device", device);
436     }
437 
438     pcmk__output_xml_push_parent(out, node);
439     out->subprocess_output(out, rc, output, error_output);
440     pcmk__output_xml_pop_parent(out);
441 
442     return rc;
443 }
444 
445 static pcmk__message_entry_t fmt_functions[] = {
446     { "failed-fencing-list", "default", stonith__failed_history },
447     { "fencing-list", "default", stonith__history },
448     { "full-fencing-list", "default", stonith__full_history },
449     { "full-fencing-list", "xml", full_history_xml },
450     { "last-fenced", "html", last_fenced_html },
451     { "last-fenced", "log", last_fenced_text },
452     { "last-fenced", "text", last_fenced_text },
453     { "last-fenced", "xml", last_fenced_xml },
454     { "pending-fencing-list", "default", stonith__pending_actions },
455     { "stonith-event", "html", stonith_event_html },
456     { "stonith-event", "log", stonith_event_text },
457     { "stonith-event", "text", stonith_event_text },
458     { "stonith-event", "xml", stonith_event_xml },
459     { "validate", "html", validate_agent_html },
460     { "validate", "log", validate_agent_text },
461     { "validate", "text", validate_agent_text },
462     { "validate", "xml", validate_agent_xml },
463 
464     { NULL, NULL, NULL }
465 };
466 
467 void
stonith__register_messages(pcmk__output_t * out)468 stonith__register_messages(pcmk__output_t *out) {
469     pcmk__register_messages(out, fmt_functions);
470 }
471