1 /*
2 * Copyright 2009-2026 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 General Public License version 2
7 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8 */
9
10 #include <crm_internal.h>
11
12 #include <stdbool.h>
13
14 #include <crm/common/mainloop.h>
15 #include <crm/common/output.h>
16 #include <crm/common/results.h>
17 #include <crm/stonith-ng.h> // stonith_t, stonith_history_t, etc.
18 #include <crm/fencing/internal.h> // stonith__*
19
20 #include <glib.h>
21 #include <libxml/tree.h>
22 #include <pacemaker.h>
23 #include <pacemaker-internal.h>
24
25 #include "libpacemaker_private.h"
26
27 static const int st_opts = st_opt_sync_call|st_opt_allow_self_fencing;
28
29 static GMainLoop *mainloop = NULL;
30
31 static struct {
32 stonith_t *st;
33 const char *target;
34 const char *action;
35 char *name;
36 unsigned int timeout;
37 unsigned int tolerance;
38 int delay;
39 pcmk__action_result_t result;
40 } async_fence_data = { NULL, };
41
42 static int
43 handle_level(stonith_t *st, const char *target, int fence_level, GList *devices,
44 bool added)
45 {
46 const char *node = NULL;
47 const char *pattern = NULL;
48
49 gchar **name_value = NULL;
50 const gchar *name = NULL;
51 const gchar *value = NULL;
52 int rc = pcmk_rc_ok;
53 char *target_copy = NULL;
54
|
(1) Event path: |
Condition "target == NULL", taking false branch. |
55 if (target == NULL) {
56 // Not really possible, but makes static analysis happy
57 return EINVAL;
58 }
59
|
(2) Event alloc_fn: |
Storage is returned from allocation function "pcmk__str_copy_as". [details] |
|
(3) Event var_assign: |
Assigning: "target_copy" = storage returned from "pcmk__str_copy_as("pcmk_fence.c", <anonymous>, 60U, target)". |
| Also see events: |
[leaked_storage] |
60 target_copy = pcmk__str_copy(target);
61
62 /* Determine if targeting by attribute, node name pattern or node name */
63 name_value = g_strsplit(target, "=", 2);
64
|
(4) Event path: |
Condition "g_strv_length(name_value) == 2", taking true branch. |
65 if (g_strv_length(name_value) == 2) {
66 name = name_value[0];
67 value = name_value[1];
68
|
(5) Event path: |
Falling through to end of if statement. |
69 } else if (*target == '@') {
70 pattern = target + 1;
71
72 } else {
73 node = target_copy;
74 }
75
76 /* Register or unregister level as appropriate */
|
(6) Event path: |
Condition "added", taking true branch. |
77 if (added) {
78 stonith_key_value_t *kvs = NULL;
79
|
(7) Event path: |
Condition "iter != NULL", taking true branch. |
|
(9) Event path: |
Condition "iter != NULL", taking false branch. |
80 for (GList *iter = devices; iter != NULL; iter = iter->next) {
81 kvs = stonith__key_value_add(kvs, NULL, iter->data);
|
(8) Event path: |
Jumping back to the beginning of the loop. |
82 }
83
84 rc = st->cmds->register_level_full(st, st_opts, node, pattern, name,
85 value, fence_level, kvs);
86 stonith__key_value_freeall(kvs, false, true);
|
(10) Event path: |
Falling through to end of if statement. |
87 } else {
88 rc = st->cmds->remove_level_full(st, st_opts, node, pattern,
89 name, value, fence_level);
90 }
91
92 g_strfreev(name_value);
|
CID (unavailable; MK=55016abac81e0011238823f90dfbdc58) (#1 of 1): Resource leak (RESOURCE_LEAK): |
|
(11) Event leaked_storage: |
Variable "target_copy" going out of scope leaks the storage it points to. |
| Also see events: |
[alloc_fn][var_assign] |
93 return pcmk_legacy2rc(rc);
94 }
95
96 static stonith_history_t *
97 reduce_fence_history(stonith_history_t *history)
98 {
99 stonith_history_t *new, *hp, *np;
100
101 if (!history) {
102 return history;
103 }
104
105 new = history;
106 hp = new->next;
107 new->next = NULL;
108
109 while (hp) {
110 stonith_history_t *hp_next = hp->next;
111
112 hp->next = NULL;
113
114 for (np = new; ; np = np->next) {
115 if ((hp->state == st_done) || (hp->state == st_failed)) {
116 /* action not in progress */
117 if (pcmk__str_eq(hp->target, np->target, pcmk__str_casei)
118 && pcmk__str_eq(hp->action, np->action, pcmk__str_none)
119 && (hp->state == np->state)
120 && ((hp->state == st_done)
121 || pcmk__str_eq(hp->delegate, np->delegate,
122 pcmk__str_casei))) {
123 /* purge older hp */
124 stonith__history_free(hp);
125 break;
126 }
127 }
128
129 if (!np->next) {
130 np->next = hp;
131 break;
132 }
133 }
134 hp = hp_next;
135 }
136
137 return new;
138 }
139
140 static void
141 notify_callback(stonith_t * st, stonith_event_t * e)
142 {
143 if (pcmk__str_eq(async_fence_data.target, e->target, pcmk__str_casei)
144 && pcmk__str_eq(async_fence_data.action, e->action, pcmk__str_none)) {
145
146 pcmk__set_result(&async_fence_data.result,
147 stonith__event_exit_status(e),
148 stonith__event_execution_status(e),
149 stonith__event_exit_reason(e));
150 g_main_loop_quit(mainloop);
151 }
152 }
153
154 static void
155 fence_callback(stonith_t * stonith, stonith_callback_data_t * data)
156 {
157 pcmk__set_result(&async_fence_data.result, stonith__exit_status(data),
158 stonith__execution_status(data),
159 stonith__exit_reason(data));
160 g_main_loop_quit(mainloop);
161 }
162
163 static gboolean
164 async_fence_helper(gpointer user_data)
165 {
166 stonith_t *st = async_fence_data.st;
167 int call_id = 0;
168 int rc = stonith__api_connect_retry(st, async_fence_data.name, 10);
169 int timeout = 0;
170
171 if (rc != pcmk_rc_ok) {
172 g_main_loop_quit(mainloop);
173 pcmk__set_result(&async_fence_data.result, CRM_EX_ERROR,
174 PCMK_EXEC_NOT_CONNECTED, pcmk_rc_str(rc));
175 return TRUE;
176 }
177
178 st->cmds->register_notification(st, PCMK__VALUE_ST_NOTIFY_FENCE,
179 notify_callback);
180
181 call_id = st->cmds->fence_with_delay(st,
182 st_opt_allow_self_fencing,
183 async_fence_data.target,
184 async_fence_data.action,
185 pcmk__timeout_ms2s(async_fence_data.timeout),
186 pcmk__timeout_ms2s(async_fence_data.tolerance),
187 async_fence_data.delay);
188
189 if (call_id < 0) {
190 g_main_loop_quit(mainloop);
191 pcmk__set_result(&async_fence_data.result, CRM_EX_ERROR,
192 PCMK_EXEC_ERROR, pcmk_strerror(call_id));
193 return TRUE;
194 }
195
196 timeout = pcmk__timeout_ms2s(async_fence_data.timeout);
197 if (async_fence_data.delay > 0) {
198 timeout += async_fence_data.delay;
199 }
200 st->cmds->register_callback(st, call_id, timeout, st_opt_timeout_updates,
201 NULL, "callback", fence_callback);
202 return TRUE;
203 }
204
205 int
206 pcmk__request_fencing(stonith_t *st, const char *target, const char *action,
207 const char *name, unsigned int timeout,
208 unsigned int tolerance, int delay, char **reason)
209 {
210 crm_trigger_t *trig;
211 int rc = pcmk_rc_ok;
212
213 async_fence_data.st = st;
214 async_fence_data.name = strdup(name);
215 async_fence_data.target = target;
216 async_fence_data.action = action;
217 async_fence_data.timeout = timeout;
218 async_fence_data.tolerance = tolerance;
219 async_fence_data.delay = delay;
220 pcmk__set_result(&async_fence_data.result, CRM_EX_ERROR, PCMK_EXEC_UNKNOWN,
221 NULL);
222
223 trig = mainloop_add_trigger(G_PRIORITY_HIGH, async_fence_helper, NULL);
224 mainloop_set_trigger(trig);
225
226 mainloop = g_main_loop_new(NULL, FALSE);
227 g_main_loop_run(mainloop);
228
229 free(async_fence_data.name);
230
231 if (reason != NULL) {
232 // Give the caller ownership of the exit reason
233 *reason = async_fence_data.result.exit_reason;
234 async_fence_data.result.exit_reason = NULL;
235 }
236 rc = stonith__result2rc(&async_fence_data.result);
237 pcmk__reset_result(&async_fence_data.result);
238 return rc;
239 }
240
241 int
242 pcmk_request_fencing(xmlNodePtr *xml, const char *target, const char *action,
243 const char *name, unsigned int timeout,
244 unsigned int tolerance, int delay, char **reason)
245 {
246 stonith_t *st = NULL;
247 pcmk__output_t *out = NULL;
248 int rc = pcmk_rc_ok;
249
250 rc = pcmk__setup_output_fencing(&out, &st, xml);
251 if (rc != pcmk_rc_ok) {
252 return rc;
253 }
254
255 rc = pcmk__request_fencing(st, target, action, name, timeout, tolerance,
256 delay, reason);
257 pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
258
259 st->cmds->disconnect(st);
260 stonith__api_free(st);
261 return rc;
262 }
263
264 int
265 pcmk__fence_history(pcmk__output_t *out, stonith_t *st, const char *target,
266 unsigned int timeout, int verbose, bool broadcast,
267 bool cleanup)
268 {
269 stonith_history_t *history = NULL;
270 stonith_history_t *latest = NULL;
271 int rc = pcmk_rc_ok;
272 int opts = 0;
273
274 if (cleanup) {
275 out->info(out, "cleaning up fencing-history%s%s",
276 target ? " for node " : "", target ? target : "");
277 }
278
279 if (broadcast) {
280 out->info(out, "gather fencing-history from all nodes");
281 }
282
283 stonith__set_call_options(opts, target, st_opts);
284
285 if (cleanup) {
286 stonith__set_call_options(opts, target, st_opt_cleanup);
287 }
288
289 if (broadcast) {
290 stonith__set_call_options(opts, target, st_opt_broadcast);
291 }
292
293 if (pcmk__str_eq(target, "*", pcmk__str_none)) {
294 target = NULL;
295 }
296
297 rc = st->cmds->history(st, opts, target, &history, pcmk__timeout_ms2s(timeout));
298
299 if (cleanup) {
300 // Cleanup doesn't return a history list
301 stonith__history_free(history);
302 return pcmk_legacy2rc(rc);
303 }
304
305 out->begin_list(out, "event", "events", "Fencing history");
306
307 history = stonith__sort_history(history);
308 for (stonith_history_t *hp = history; hp != NULL; hp = hp->next) {
309 if (hp->state == st_done) {
310 latest = hp;
311 }
312
313 if (out->is_quiet(out) || !verbose) {
314 continue;
315 }
316
317 out->message(out, "stonith-event", hp, true, false,
318 stonith__later_succeeded(hp, history),
319 (uint32_t) pcmk_show_failed_detail);
320 out->increment_list(out);
321 }
322
323 if (latest) {
324 if (out->is_quiet(out)) {
325 out->message(out, "stonith-event", latest, false, true, NULL,
326 (uint32_t) pcmk_show_failed_detail);
327 } else if (!verbose) { // already printed if verbose
328 out->message(out, "stonith-event", latest, false, false, NULL,
329 (uint32_t) pcmk_show_failed_detail);
330 out->increment_list(out);
331 }
332 }
333
334 out->end_list(out);
335
336 stonith__history_free(history);
337 return pcmk_legacy2rc(rc);
338 }
339
340 int
341 pcmk_fence_history(xmlNodePtr *xml, const char *target, unsigned int timeout,
342 bool quiet, int verbose, bool broadcast, bool cleanup)
343 {
344 stonith_t *st = NULL;
345 pcmk__output_t *out = NULL;
346 int rc = pcmk_rc_ok;
347
348 rc = pcmk__setup_output_fencing(&out, &st, xml);
349 if (rc != pcmk_rc_ok) {
350 return rc;
351 }
352
353 out->quiet = quiet;
354
355 rc = pcmk__fence_history(out, st, target, timeout, verbose, broadcast,
356 cleanup);
357 pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
358
359 st->cmds->disconnect(st);
360 stonith__api_free(st);
361 return rc;
362 }
363
364 int
365 pcmk__fence_installed(pcmk__output_t *out, stonith_t *st)
366 {
367 stonith_key_value_t *devices = NULL;
368 int rc = pcmk_rc_ok;
369
370 rc = st->cmds->list_agents(st, st_opt_sync_call, NULL, &devices, 0);
371 // rc is a negative error code or a positive number of agents
372 if (rc < 0) {
373 return pcmk_legacy2rc(rc);
374 }
375
376 out->begin_list(out, "fence device", "fence devices",
377 "Installed fence devices");
378 for (stonith_key_value_t *iter = devices; iter != NULL; iter = iter->next) {
379 out->list_item(out, "device", "%s", iter->value);
380 }
381 out->end_list(out);
382
383 stonith__key_value_freeall(devices, true, true);
384 return pcmk_rc_ok;
385 }
386
387 int
388 pcmk_fence_installed(xmlNodePtr *xml, unsigned int timeout)
389 {
390 stonith_t *st = NULL;
391 pcmk__output_t *out = NULL;
392 int rc = pcmk_rc_ok;
393
394 rc = pcmk__setup_output_fencing(&out, &st, xml);
395 if (rc != pcmk_rc_ok) {
396 return rc;
397 }
398
399 rc = pcmk__fence_installed(out, st);
400 pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
401
402 st->cmds->disconnect(st);
403 stonith__api_free(st);
404 return rc;
405 }
406
407 int
408 pcmk__fence_last(pcmk__output_t *out, const char *target, bool as_nodeid)
409 {
410 time_t when = 0;
411
412 if (target == NULL) {
413 return pcmk_rc_ok;
414 }
415
416 if (as_nodeid) {
417 when = stonith_api_time(atol(target), NULL, FALSE);
418 } else {
419 when = stonith_api_time(0, target, FALSE);
420 }
421
422 return out->message(out, "last-fenced", target, when);
423 }
424
425 int
426 pcmk_fence_last(xmlNodePtr *xml, const char *target, bool as_nodeid)
427 {
428 pcmk__output_t *out = NULL;
429 int rc = pcmk_rc_ok;
430
431 rc = pcmk__xml_output_new(&out, xml);
432 if (rc != pcmk_rc_ok) {
433 return rc;
434 }
435
436 stonith__register_messages(out);
437
438 rc = pcmk__fence_last(out, target, as_nodeid);
439 pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
440 return rc;
441 }
442
443 int
444 pcmk__fence_list_targets(pcmk__output_t *out, stonith_t *st,
445 const char *device_id, unsigned int timeout)
446 {
447 GList *targets = NULL;
448 char *lists = NULL;
449 int rc = pcmk_rc_ok;
450
451 rc = st->cmds->list(st, st_opts, device_id, &lists, pcmk__timeout_ms2s(timeout));
452 if (rc != pcmk_rc_ok) {
453 return pcmk_legacy2rc(rc);
454 }
455
456 targets = stonith__parse_targets(lists);
457
458 out->begin_list(out, "fence target", "fence targets", "Fence Targets");
459 while (targets != NULL) {
460 out->list_item(out, NULL, "%s", (const char *) targets->data);
461 targets = targets->next;
462 }
463 out->end_list(out);
464
465 free(lists);
466 return rc;
467 }
468
469 int
470 pcmk_fence_list_targets(xmlNodePtr *xml, const char *device_id, unsigned int timeout)
471 {
472 stonith_t *st = NULL;
473 pcmk__output_t *out = NULL;
474 int rc = pcmk_rc_ok;
475
476 rc = pcmk__setup_output_fencing(&out, &st, xml);
477 if (rc != pcmk_rc_ok) {
478 return rc;
479 }
480
481 rc = pcmk__fence_list_targets(out, st, device_id, timeout);
482 pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
483
484 st->cmds->disconnect(st);
485 stonith__api_free(st);
486 return rc;
487 }
488
489 int
490 pcmk__fence_metadata(pcmk__output_t *out, stonith_t *st, const char *agent,
491 unsigned int timeout)
492 {
493 char *buffer = NULL;
494 int rc = st->cmds->metadata(st, st_opt_sync_call, agent, NULL, &buffer,
495 pcmk__timeout_ms2s(timeout));
496
497 if (rc != pcmk_rc_ok) {
498 return pcmk_legacy2rc(rc);
499 }
500
501 out->output_xml(out, PCMK_XE_METADATA, buffer);
502 free(buffer);
503 return rc;
504 }
505
506 int
507 pcmk_fence_metadata(xmlNodePtr *xml, const char *agent, unsigned int timeout)
508 {
509 stonith_t *st = NULL;
510 pcmk__output_t *out = NULL;
511 int rc = pcmk_rc_ok;
512
513 rc = pcmk__setup_output_fencing(&out, &st, xml);
514 if (rc != pcmk_rc_ok) {
515 return rc;
516 }
517
518 rc = pcmk__fence_metadata(out, st, agent, timeout);
519 pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
520
521 st->cmds->disconnect(st);
522 stonith__api_free(st);
523 return rc;
524 }
525
526 int
527 pcmk__fence_registered(pcmk__output_t *out, stonith_t *st, const char *target,
528 unsigned int timeout)
529 {
530 stonith_key_value_t *devices = NULL;
531 int rc = pcmk_rc_ok;
532
533 rc = st->cmds->query(st, st_opts, target, &devices, pcmk__timeout_ms2s(timeout));
534 /* query returns a negative error code or a positive number of results. */
535 if (rc < 0) {
536 return pcmk_legacy2rc(rc);
537 }
538
539 out->begin_list(out, "fence device", "fence devices",
540 "Registered fence devices");
541 for (stonith_key_value_t *iter = devices; iter != NULL; iter = iter->next) {
542 out->list_item(out, "device", "%s", iter->value);
543 }
544 out->end_list(out);
545
546 stonith__key_value_freeall(devices, true, true);
547
548 /* Return pcmk_rc_ok here, not the number of results. Callers probably
549 * don't care.
550 */
551 return pcmk_rc_ok;
552 }
553
554 int
555 pcmk_fence_registered(xmlNodePtr *xml, const char *target, unsigned int timeout)
556 {
557 stonith_t *st = NULL;
558 pcmk__output_t *out = NULL;
559 int rc = pcmk_rc_ok;
560
561 rc = pcmk__setup_output_fencing(&out, &st, xml);
562 if (rc != pcmk_rc_ok) {
563 return rc;
564 }
565
566 rc = pcmk__fence_registered(out, st, target, timeout);
567 pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
568
569 st->cmds->disconnect(st);
570 stonith__api_free(st);
571 return rc;
572 }
573
574 int
575 pcmk__fence_register_level(stonith_t *st, const char *target, int fence_level,
576 GList *devices)
577 {
578 return handle_level(st, target, fence_level, devices, true);
579 }
580
581 int
582 pcmk_fence_register_level(xmlNodePtr *xml, const char *target, int fence_level,
583 GList *devices)
584 {
585 stonith_t* st = NULL;
586 pcmk__output_t *out = NULL;
587 int rc = pcmk_rc_ok;
588
589 rc = pcmk__setup_output_fencing(&out, &st, xml);
590 if (rc != pcmk_rc_ok) {
591 return rc;
592 }
593
594 rc = pcmk__fence_register_level(st, target, fence_level, devices);
595 pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
596
597 st->cmds->disconnect(st);
598 stonith__api_free(st);
599 return rc;
600 }
601
602 int
603 pcmk__fence_unregister_level(stonith_t *st, const char *target, int fence_level)
604 {
605 return handle_level(st, target, fence_level, NULL, false);
606 }
607
608 int
609 pcmk_fence_unregister_level(xmlNodePtr *xml, const char *target, int fence_level)
610 {
611 stonith_t* st = NULL;
612 pcmk__output_t *out = NULL;
613 int rc = pcmk_rc_ok;
614
615 rc = pcmk__setup_output_fencing(&out, &st, xml);
616 if (rc != pcmk_rc_ok) {
617 return rc;
618 }
619
620 rc = pcmk__fence_unregister_level(st, target, fence_level);
621 pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
622
623 st->cmds->disconnect(st);
624 stonith__api_free(st);
625 return rc;
626 }
627
628 int
629 pcmk__fence_validate(pcmk__output_t *out, stonith_t *st, const char *agent,
630 const char *id, GHashTable *params, unsigned int timeout)
631 {
632 char *output = NULL;
633 char *error_output = NULL;
634 int rc;
635
636 rc = stonith__validate(st, st_opt_sync_call, id, agent, params,
637 pcmk__timeout_ms2s(timeout), &output, &error_output);
638 out->message(out, "validate", agent, id, output, error_output, rc);
639 return pcmk_legacy2rc(rc);
640 }
641
642 int
643 pcmk_fence_validate(xmlNodePtr *xml, const char *agent, const char *id,
644 GHashTable *params, unsigned int timeout)
645 {
646 stonith_t *st = NULL;
647 pcmk__output_t *out = NULL;
648 int rc = pcmk_rc_ok;
649
650 rc = pcmk__setup_output_fencing(&out, &st, xml);
651 if (rc != pcmk_rc_ok) {
652 return rc;
653 }
654
655 rc = pcmk__fence_validate(out, st, agent, id, params, timeout);
656 pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
657
658 st->cmds->disconnect(st);
659 stonith__api_free(st);
660 return rc;
661 }
662
663 int
664 pcmk__get_fencing_history(stonith_t *st, stonith_history_t **stonith_history,
665 enum pcmk__fence_history fence_history)
666 {
667 int rc = pcmk_rc_ok;
668
669 if ((st == NULL) || (st->state == stonith_disconnected)) {
670 rc = ENOTCONN;
671 } else if (fence_history != pcmk__fence_history_none) {
672 rc = st->cmds->history(st, st_opt_sync_call, NULL, stonith_history,
673 120);
674
675 rc = pcmk_legacy2rc(rc);
676 if (rc != pcmk_rc_ok) {
677 return rc;
678 }
679
680 *stonith_history = stonith__sort_history(*stonith_history);
681 if (fence_history == pcmk__fence_history_reduced) {
682 *stonith_history = reduce_fence_history(*stonith_history);
683 }
684 }
685
686 return rc;
687 }
688