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