1 /*
2 * Copyright 2004-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 Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 */
9
10 #include <crm_internal.h>
11
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <stdbool.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include <inttypes.h>
18 #include <sys/types.h>
19 #include <glib.h>
20
21 #include <crm/crm.h>
22 #include <crm/stonith-ng.h>
23 #include <crm/fencing/internal.h>
24 #include <crm/common/xml.h>
25
26 #include <crm/common/mainloop.h>
27
28 #include "fencing_private.h"
29
30 CRM_TRACE_INIT_DATA(stonith);
31
32 // Used as stonith_t:st_private
33 typedef struct stonith_private_s {
34 char *token;
35 crm_ipc_t *ipc;
36 mainloop_io_t *source;
37 GHashTable *stonith_op_callback_table;
38 GList *notify_list;
39 int notify_refcnt;
40 bool notify_deletes;
41
42 void (*op_callback) (stonith_t * st, stonith_callback_data_t * data);
43
44 } stonith_private_t;
45
46 // Used as stonith_event_t:opaque
47 struct event_private {
48 pcmk__action_result_t result;
49 };
50
51 typedef struct stonith_notify_client_s {
52 const char *event;
53 const char *obj_id; /* implement one day */
54 const char *obj_type; /* implement one day */
55 void (*notify) (stonith_t * st, stonith_event_t * e);
56 bool delete;
57
58 } stonith_notify_client_t;
59
60 typedef struct stonith_callback_client_s {
61 void (*callback) (stonith_t * st, stonith_callback_data_t * data);
62 const char *id;
63 void *user_data;
64 gboolean only_success;
65 gboolean allow_timeout_updates;
66 struct timer_rec_s *timer;
67
68 } stonith_callback_client_t;
69
70 struct notify_blob_s {
71 stonith_t *stonith;
72 xmlNode *xml;
73 };
74
75 struct timer_rec_s {
76 int call_id;
77 int timeout;
78 guint ref;
79 stonith_t *stonith;
80 };
81
82 typedef int (*stonith_op_t) (const char *, int, const char *, xmlNode *,
83 xmlNode *, xmlNode *, xmlNode **, xmlNode **);
84
85 bool stonith_dispatch(stonith_t * st);
86 xmlNode *stonith_create_op(int call_id, const char *token, const char *op, xmlNode * data,
87 int call_options);
88 static int stonith_send_command(stonith_t *stonith, const char *op,
89 xmlNode *data, xmlNode **output_data,
90 int call_options, int timeout);
91
92 static void stonith_connection_destroy(gpointer user_data);
93 static void stonith_send_notification(gpointer data, gpointer user_data);
94 static int stonith_api_del_notification(stonith_t *stonith,
95 const char *event);
96 /*!
97 * \brief Get agent namespace by name
98 *
99 * \param[in] namespace_s Name of namespace as string
100 *
101 * \return Namespace as enum value
102 */
103 enum stonith_namespace
104 stonith_text2namespace(const char *namespace_s)
105 {
106 if (pcmk__str_eq(namespace_s, "any", pcmk__str_null_matches)) {
107 return st_namespace_any;
108
109 } else if (!strcmp(namespace_s, "redhat")
110 || !strcmp(namespace_s, "stonith-ng")) {
111 return st_namespace_rhcs;
112
113 } else if (!strcmp(namespace_s, "internal")) {
114 return st_namespace_internal;
115
116 } else if (!strcmp(namespace_s, "heartbeat")) {
117 return st_namespace_lha;
118 }
119 return st_namespace_invalid;
120 }
121
122 /*!
123 * \brief Get agent namespace name
124 *
125 * \param[in] namespace Namespace as enum value
126 *
127 * \return Namespace name as string
128 */
129 const char *
130 stonith_namespace2text(enum stonith_namespace st_namespace)
131 {
132 switch (st_namespace) {
133 case st_namespace_any: return "any";
134 case st_namespace_rhcs: return "stonith-ng";
135 case st_namespace_internal: return "internal";
136 case st_namespace_lha: return "heartbeat";
137 default: break;
138 }
139 return "unsupported";
140 }
141
142 /*!
143 * \brief Determine namespace of a fence agent
144 *
145 * \param[in] agent Fence agent type
146 * \param[in] namespace_s Name of agent namespace as string, if known
147 *
148 * \return Namespace of specified agent, as enum value
149 */
150 enum stonith_namespace
151 stonith_get_namespace(const char *agent, const char *namespace_s)
152 {
153 if (pcmk__str_eq(namespace_s, "internal", pcmk__str_none)) {
154 return st_namespace_internal;
155 }
156
157 if (stonith__agent_is_rhcs(agent)) {
158 return st_namespace_rhcs;
159 }
160
161 #if HAVE_STONITH_STONITH_H
162 if (stonith__agent_is_lha(agent)) {
163 return st_namespace_lha;
164 }
165 #endif
166
167 return st_namespace_invalid;
168 }
169
170 gboolean
171 stonith__watchdog_fencing_enabled_for_node_api(stonith_t *st, const char *node)
172 {
173 gboolean rv = FALSE;
174 stonith_t *stonith_api = st?st:stonith_api_new();
175 char *list = NULL;
176
177 if(stonith_api) {
178 if (stonith_api->state == stonith_disconnected) {
179 int rc = stonith_api->cmds->connect(stonith_api, "stonith-api", NULL);
180
181 if (rc != pcmk_ok) {
182 crm_err("Failed connecting to Stonith-API for watchdog-fencing-query.");
183 }
184 }
185
186 if (stonith_api->state != stonith_disconnected) {
187 /* caveat!!!
188 * this might fail when when stonithd is just updating the device-list
189 * probably something we should fix as well for other api-calls */
190 int rc = stonith_api->cmds->list(stonith_api, st_opt_sync_call, STONITH_WATCHDOG_ID, &list, 0);
191 if ((rc != pcmk_ok) || (list == NULL)) {
192 /* due to the race described above it can happen that
193 * we drop in here - so as not to make remote nodes
194 * panic on that answer
195 */
196 if (rc == -ENODEV) {
197 crm_notice("Cluster does not have watchdog fencing device");
198 } else {
199 crm_warn("Could not check for watchdog fencing device: %s",
200 pcmk_strerror(rc));
201 }
202 } else if (list[0] == '\0') {
203 rv = TRUE;
204 } else {
205 GList *targets = stonith__parse_targets(list);
206 rv = pcmk__str_in_list(node, targets, pcmk__str_casei);
207 g_list_free_full(targets, free);
208 }
209 free(list);
210 if (!st) {
211 /* if we're provided the api we still might have done the
212 * connection - but let's assume the caller won't bother
213 */
214 stonith_api->cmds->disconnect(stonith_api);
215 }
216 }
217
218 if (!st) {
219 stonith_api_delete(stonith_api);
220 }
221 } else {
222 crm_err("Stonith-API for watchdog-fencing-query couldn't be created.");
223 }
224 crm_trace("Pacemaker assumes node %s %sto do watchdog-fencing.",
225 node, rv?"":"not ");
226 return rv;
227 }
228
229 gboolean
230 stonith__watchdog_fencing_enabled_for_node(const char *node)
231 {
232 return stonith__watchdog_fencing_enabled_for_node_api(NULL, node);
233 }
234
235 /* when cycling through the list we don't want to delete items
236 so just mark them and when we know nobody is using the list
237 loop over it to remove the marked items
238 */
239 static void
240 foreach_notify_entry (stonith_private_t *private,
241 GFunc func,
242 gpointer user_data)
243 {
244 private->notify_refcnt++;
245 g_list_foreach(private->notify_list, func, user_data);
246 private->notify_refcnt--;
247 if ((private->notify_refcnt == 0) &&
248 private->notify_deletes) {
249 GList *list_item = private->notify_list;
250
251 private->notify_deletes = FALSE;
252 while (list_item != NULL)
253 {
254 stonith_notify_client_t *list_client = list_item->data;
255 GList *next = g_list_next(list_item);
256
257 if (list_client->delete) {
258 free(list_client);
259 private->notify_list =
260 g_list_delete_link(private->notify_list, list_item);
261 }
262 list_item = next;
263 }
264 }
265 }
266
267 static void
268 stonith_connection_destroy(gpointer user_data)
269 {
270 stonith_t *stonith = user_data;
271 stonith_private_t *native = NULL;
272 struct notify_blob_s blob;
273
274 crm_trace("Sending destroyed notification");
275 blob.stonith = stonith;
276 blob.xml = pcmk__xe_create(NULL, PCMK__XE_NOTIFY);
277
278 native = stonith->st_private;
279 native->ipc = NULL;
280 native->source = NULL;
281
282 free(native->token); native->token = NULL;
283 stonith->state = stonith_disconnected;
284 crm_xml_add(blob.xml, PCMK__XA_T, PCMK__VALUE_ST_NOTIFY);
285 crm_xml_add(blob.xml, PCMK__XA_SUBT, PCMK__VALUE_ST_NOTIFY_DISCONNECT);
286
287 foreach_notify_entry(native, stonith_send_notification, &blob);
288 pcmk__xml_free(blob.xml);
289 }
290
291 xmlNode *
292 create_device_registration_xml(const char *id, enum stonith_namespace standard,
293 const char *agent,
294 const stonith_key_value_t *params,
295 const char *rsc_provides)
296 {
297 xmlNode *data = pcmk__xe_create(NULL, PCMK__XE_ST_DEVICE_ID);
298 xmlNode *args = pcmk__xe_create(data, PCMK__XE_ATTRIBUTES);
299
300 #if HAVE_STONITH_STONITH_H
301 if (standard == st_namespace_any) {
302 standard = stonith_get_namespace(agent, NULL);
303 }
304 if (standard == st_namespace_lha) {
305 hash2field((gpointer) "plugin", (gpointer) agent, args);
306 agent = "fence_legacy";
307 }
308 #endif
309
310 crm_xml_add(data, PCMK_XA_ID, id);
311 crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__);
312 crm_xml_add(data, PCMK_XA_AGENT, agent);
313 if ((standard != st_namespace_any) && (standard != st_namespace_invalid)) {
314 crm_xml_add(data, PCMK__XA_NAMESPACE,
315 stonith_namespace2text(standard));
316 }
317 if (rsc_provides) {
318 crm_xml_add(data, PCMK__XA_RSC_PROVIDES, rsc_provides);
319 }
320
321 for (; params; params = params->next) {
322 hash2field((gpointer) params->key, (gpointer) params->value, args);
323 }
324
325 return data;
326 }
327
328 static int
329 stonith_api_register_device(stonith_t *st, int call_options,
330 const char *id, const char *namespace_s,
331 const char *agent,
332 const stonith_key_value_t *params)
333 {
334 int rc = 0;
335 xmlNode *data = NULL;
336
337 data = create_device_registration_xml(id,
338 stonith_text2namespace(namespace_s),
339 agent, params, NULL);
340
341 rc = stonith_send_command(st, STONITH_OP_DEVICE_ADD, data, NULL, call_options, 0);
342 pcmk__xml_free(data);
343
344 return rc;
345 }
346
347 static int
348 stonith_api_remove_device(stonith_t * st, int call_options, const char *name)
349 {
350 int rc = 0;
351 xmlNode *data = NULL;
352
353 data = pcmk__xe_create(NULL, PCMK__XE_ST_DEVICE_ID);
354 crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__);
355 crm_xml_add(data, PCMK_XA_ID, name);
356 rc = stonith_send_command(st, STONITH_OP_DEVICE_DEL, data, NULL, call_options, 0);
357 pcmk__xml_free(data);
358
359 return rc;
360 }
361
362 static int
363 stonith_api_remove_level_full(stonith_t *st, int options,
364 const char *node, const char *pattern,
365 const char *attr, const char *value, int level)
366 {
367 int rc = 0;
368 xmlNode *data = NULL;
369
370 CRM_CHECK(node || pattern || (attr && value), return -EINVAL);
371
372 data = pcmk__xe_create(NULL, PCMK_XE_FENCING_LEVEL);
373 crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__);
374
375 if (node) {
376 crm_xml_add(data, PCMK_XA_TARGET, node);
377
378 } else if (pattern) {
379 crm_xml_add(data, PCMK_XA_TARGET_PATTERN, pattern);
380
381 } else {
382 crm_xml_add(data, PCMK_XA_TARGET_ATTRIBUTE, attr);
383 crm_xml_add(data, PCMK_XA_TARGET_VALUE, value);
384 }
385
386 crm_xml_add_int(data, PCMK_XA_INDEX, level);
387 rc = stonith_send_command(st, STONITH_OP_LEVEL_DEL, data, NULL, options, 0);
388 pcmk__xml_free(data);
389
390 return rc;
391 }
392
393 static int
394 stonith_api_remove_level(stonith_t * st, int options, const char *node, int level)
395 {
396 return stonith_api_remove_level_full(st, options, node,
397 NULL, NULL, NULL, level);
398 }
399
400 /*!
401 * \internal
402 * \brief Create XML for fence topology level registration request
403 *
404 * \param[in] node If not NULL, target level by this node name
405 * \param[in] pattern If not NULL, target by node name using this regex
406 * \param[in] attr If not NULL, target by this node attribute
407 * \param[in] value If not NULL, target by this node attribute value
408 * \param[in] level Index number of level to register
409 * \param[in] device_list List of devices in level
410 *
411 * \return Newly allocated XML tree on success, NULL otherwise
412 *
413 * \note The caller should set only one of node, pattern or attr/value.
414 */
415 xmlNode *
416 create_level_registration_xml(const char *node, const char *pattern,
417 const char *attr, const char *value,
418 int level, const stonith_key_value_t *device_list)
419 {
420 GString *list = NULL;
421 xmlNode *data;
422
423 CRM_CHECK(node || pattern || (attr && value), return NULL);
424
425 data = pcmk__xe_create(NULL, PCMK_XE_FENCING_LEVEL);
426
427 crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__);
428 crm_xml_add_int(data, PCMK_XA_ID, level);
429 crm_xml_add_int(data, PCMK_XA_INDEX, level);
430
431 if (node) {
432 crm_xml_add(data, PCMK_XA_TARGET, node);
433
434 } else if (pattern) {
435 crm_xml_add(data, PCMK_XA_TARGET_PATTERN, pattern);
436
437 } else {
438 crm_xml_add(data, PCMK_XA_TARGET_ATTRIBUTE, attr);
439 crm_xml_add(data, PCMK_XA_TARGET_VALUE, value);
440 }
441
442 for (; device_list; device_list = device_list->next) {
443 pcmk__add_separated_word(&list, 1024, device_list->value, ",");
444 }
445
446 if (list != NULL) {
447 crm_xml_add(data, PCMK_XA_DEVICES, (const char *) list->str);
448 g_string_free(list, TRUE);
449 }
450 return data;
451 }
452
453 static int
454 stonith_api_register_level_full(stonith_t *st, int options, const char *node,
455 const char *pattern, const char *attr,
456 const char *value, int level,
457 const stonith_key_value_t *device_list)
458 {
459 int rc = 0;
460 xmlNode *data = create_level_registration_xml(node, pattern, attr, value,
461 level, device_list);
462 CRM_CHECK(data != NULL, return -EINVAL);
463
464 rc = stonith_send_command(st, STONITH_OP_LEVEL_ADD, data, NULL, options, 0);
465 pcmk__xml_free(data);
466
467 return rc;
468 }
469
470 static int
471 stonith_api_register_level(stonith_t * st, int options, const char *node, int level,
472 const stonith_key_value_t * device_list)
473 {
474 return stonith_api_register_level_full(st, options, node, NULL, NULL, NULL,
475 level, device_list);
476 }
477
478 static int
479 stonith_api_device_list(stonith_t *stonith, int call_options,
480 const char *namespace_s, stonith_key_value_t **devices,
481 int timeout)
482 {
483 int count = 0;
484 enum stonith_namespace ns = stonith_text2namespace(namespace_s);
485
486 if (devices == NULL) {
487 crm_err("Parameter error: stonith_api_device_list");
488 return -EFAULT;
489 }
490
491 #if HAVE_STONITH_STONITH_H
492 // Include Linux-HA agents if requested
493 if ((ns == st_namespace_any) || (ns == st_namespace_lha)) {
494 count += stonith__list_lha_agents(devices);
495 }
496 #endif
497
498 // Include Red Hat agents if requested
499 if ((ns == st_namespace_any) || (ns == st_namespace_rhcs)) {
500 count += stonith__list_rhcs_agents(devices);
501 }
502
503 return count;
504 }
505
506 // See stonith_api_operations_t:metadata() documentation
507 static int
508 stonith_api_device_metadata(stonith_t *stonith, int call_options,
509 const char *agent, const char *namespace_s,
510 char **output, int timeout_sec)
511 {
512 /* By executing meta-data directly, we can get it from stonith_admin when
513 * the cluster is not running, which is important for higher-level tools.
514 */
515
516 enum stonith_namespace ns = stonith_get_namespace(agent, namespace_s);
517
518 if (timeout_sec <= 0) {
519 timeout_sec = PCMK_DEFAULT_ACTION_TIMEOUT_MS;
520 }
521
522 crm_trace("Looking up metadata for %s agent %s",
523 stonith_namespace2text(ns), agent);
524
525 switch (ns) {
526 case st_namespace_rhcs:
527 return stonith__rhcs_metadata(agent, timeout_sec, output);
528
529 #if HAVE_STONITH_STONITH_H
530 case st_namespace_lha:
531 return stonith__lha_metadata(agent, timeout_sec, output);
532 #endif
533
534 default:
535 crm_err("Can't get fence agent '%s' meta-data: No such agent",
536 agent);
537 break;
538 }
539 return -ENODEV;
540 }
541
542 static int
543 stonith_api_query(stonith_t * stonith, int call_options, const char *target,
544 stonith_key_value_t ** devices, int timeout)
545 {
546 int rc = 0, lpc = 0, max = 0;
547
548 xmlNode *data = NULL;
549 xmlNode *output = NULL;
550 xmlXPathObjectPtr xpathObj = NULL;
551
552 CRM_CHECK(devices != NULL, return -EINVAL);
553
554 data = pcmk__xe_create(NULL, PCMK__XE_ST_DEVICE_ID);
555 crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__);
556 crm_xml_add(data, PCMK__XA_ST_TARGET, target);
557 crm_xml_add(data, PCMK__XA_ST_DEVICE_ACTION, PCMK_ACTION_OFF);
558 rc = stonith_send_command(stonith, STONITH_OP_QUERY, data, &output, call_options, timeout);
559
560 if (rc < 0) {
561 return rc;
562 }
563
564 xpathObj = xpath_search(output, "//@agent");
565 if (xpathObj) {
566 max = numXpathResults(xpathObj);
567
568 for (lpc = 0; lpc < max; lpc++) {
569 xmlNode *match = getXpathResult(xpathObj, lpc);
570
571 CRM_LOG_ASSERT(match != NULL);
572 if(match != NULL) {
573 xmlChar *match_path = xmlGetNodePath(match);
574
575 crm_info("%s[%d] = %s", "//@agent", lpc, match_path);
576 free(match_path);
577 *devices = stonith_key_value_add(*devices, NULL,
578 crm_element_value(match,
579 PCMK_XA_ID));
580 }
581 }
582
583 freeXpathObject(xpathObj);
584 }
585
586 pcmk__xml_free(output);
587 pcmk__xml_free(data);
588 return max;
589 }
590
591 /*!
592 * \internal
593 * \brief Make a STONITH_OP_EXEC request
594 *
595 * \param[in,out] stonith Fencer connection
596 * \param[in] call_options Bitmask of \c stonith_call_options
597 * \param[in] id Fence device ID that request is for
598 * \param[in] action Agent action to request (list, status, monitor)
599 * \param[in] target Name of target node for requested action
600 * \param[in] timeout_sec Error if not completed within this many seconds
601 * \param[out] output Where to set agent output
602 */
603 static int
604 stonith_api_call(stonith_t *stonith, int call_options, const char *id,
605 const char *action, const char *target, int timeout_sec,
606 xmlNode **output)
607 {
608 int rc = 0;
609 xmlNode *data = NULL;
610
611 data = pcmk__xe_create(NULL, PCMK__XE_ST_DEVICE_ID);
612 crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__);
613 crm_xml_add(data, PCMK__XA_ST_DEVICE_ID, id);
614 crm_xml_add(data, PCMK__XA_ST_DEVICE_ACTION, action);
615 crm_xml_add(data, PCMK__XA_ST_TARGET, target);
616
617 rc = stonith_send_command(stonith, STONITH_OP_EXEC, data, output,
618 call_options, timeout_sec);
619 pcmk__xml_free(data);
620
621 return rc;
622 }
623
624 static int
625 stonith_api_list(stonith_t * stonith, int call_options, const char *id, char **list_info,
626 int timeout)
627 {
628 int rc;
629 xmlNode *output = NULL;
630
631 rc = stonith_api_call(stonith, call_options, id, PCMK_ACTION_LIST, NULL,
632 timeout, &output);
633
634 if (output && list_info) {
635 const char *list_str;
636
637 list_str = crm_element_value(output, PCMK__XA_ST_OUTPUT);
638
639 if (list_str) {
640 *list_info = strdup(list_str);
641 }
642 }
643
644 if (output) {
645 pcmk__xml_free(output);
646 }
647
648 return rc;
649 }
650
651 static int
652 stonith_api_monitor(stonith_t * stonith, int call_options, const char *id, int timeout)
653 {
654 return stonith_api_call(stonith, call_options, id, PCMK_ACTION_MONITOR,
655 NULL, timeout, NULL);
656 }
657
658 static int
659 stonith_api_status(stonith_t * stonith, int call_options, const char *id, const char *port,
660 int timeout)
661 {
662 return stonith_api_call(stonith, call_options, id, PCMK_ACTION_STATUS, port,
663 timeout, NULL);
664 }
665
666 static int
667 stonith_api_fence_with_delay(stonith_t * stonith, int call_options, const char *node,
668 const char *action, int timeout, int tolerance, int delay)
669 {
670 int rc = 0;
671 xmlNode *data = NULL;
672
673 data = pcmk__xe_create(NULL, __func__);
674 crm_xml_add(data, PCMK__XA_ST_TARGET, node);
675 crm_xml_add(data, PCMK__XA_ST_DEVICE_ACTION, action);
676 crm_xml_add_int(data, PCMK__XA_ST_TIMEOUT, timeout);
677 crm_xml_add_int(data, PCMK__XA_ST_TOLERANCE, tolerance);
678 crm_xml_add_int(data, PCMK__XA_ST_DELAY, delay);
679
680 rc = stonith_send_command(stonith, STONITH_OP_FENCE, data, NULL, call_options, timeout);
681 pcmk__xml_free(data);
682
683 return rc;
684 }
685
686 static int
687 stonith_api_fence(stonith_t * stonith, int call_options, const char *node, const char *action,
688 int timeout, int tolerance)
689 {
690 return stonith_api_fence_with_delay(stonith, call_options, node, action,
691 timeout, tolerance, 0);
692 }
693
694 static int
695 stonith_api_confirm(stonith_t * stonith, int call_options, const char *target)
696 {
697 stonith__set_call_options(call_options, target, st_opt_manual_ack);
698 return stonith_api_fence(stonith, call_options, target, PCMK_ACTION_OFF, 0,
699 0);
700 }
701
702 static int
703 stonith_api_history(stonith_t * stonith, int call_options, const char *node,
704 stonith_history_t ** history, int timeout)
705 {
706 int rc = 0;
707 xmlNode *data = NULL;
708 xmlNode *output = NULL;
709 stonith_history_t *last = NULL;
710
711 *history = NULL;
712
713 if (node) {
714 data = pcmk__xe_create(NULL, __func__);
715 crm_xml_add(data, PCMK__XA_ST_TARGET, node);
716 }
717
718 stonith__set_call_options(call_options, node, st_opt_sync_call);
719 rc = stonith_send_command(stonith, STONITH_OP_FENCE_HISTORY, data, &output,
720 call_options, timeout);
721 pcmk__xml_free(data);
722
723 if (rc == 0) {
724 xmlNode *op = NULL;
725 xmlNode *reply = get_xpath_object("//" PCMK__XE_ST_HISTORY, output,
726 LOG_NEVER);
727
728 for (op = pcmk__xe_first_child(reply, NULL, NULL, NULL); op != NULL;
729 op = pcmk__xe_next(op, NULL)) {
730 stonith_history_t *kvp;
731 long long completed;
732 long long completed_nsec = 0L;
733
734 kvp = pcmk__assert_alloc(1, sizeof(stonith_history_t));
735 kvp->target = crm_element_value_copy(op, PCMK__XA_ST_TARGET);
736 kvp->action = crm_element_value_copy(op, PCMK__XA_ST_DEVICE_ACTION);
737 kvp->origin = crm_element_value_copy(op, PCMK__XA_ST_ORIGIN);
738 kvp->delegate = crm_element_value_copy(op, PCMK__XA_ST_DELEGATE);
739 kvp->client = crm_element_value_copy(op, PCMK__XA_ST_CLIENTNAME);
740 crm_element_value_ll(op, PCMK__XA_ST_DATE, &completed);
741 kvp->completed = (time_t) completed;
742 crm_element_value_ll(op, PCMK__XA_ST_DATE_NSEC, &completed_nsec);
743 kvp->completed_nsec = completed_nsec;
744 crm_element_value_int(op, PCMK__XA_ST_STATE, &kvp->state);
745 kvp->exit_reason = crm_element_value_copy(op, PCMK_XA_EXIT_REASON);
746
747 if (last) {
748 last->next = kvp;
749 } else {
750 *history = kvp;
751 }
752 last = kvp;
753 }
754 }
755
756 pcmk__xml_free(output);
757
758 return rc;
759 }
760
761 void stonith_history_free(stonith_history_t *history)
762 {
763 stonith_history_t *hp, *hp_old;
764
765 for (hp = history; hp; hp_old = hp, hp = hp->next, free(hp_old)) {
766 free(hp->target);
767 free(hp->action);
768 free(hp->origin);
769 free(hp->delegate);
770 free(hp->client);
771 free(hp->exit_reason);
772 }
773 }
774
775 static gint
776 stonithlib_GCompareFunc(gconstpointer a, gconstpointer b)
777 {
778 int rc = 0;
779 const stonith_notify_client_t *a_client = a;
780 const stonith_notify_client_t *b_client = b;
781
782 if (a_client->delete || b_client->delete) {
783 /* make entries marked for deletion not findable */
784 return -1;
785 }
786 CRM_CHECK(a_client->event != NULL && b_client->event != NULL, return 0);
787 rc = strcmp(a_client->event, b_client->event);
788 if (rc == 0) {
789 if (a_client->notify == NULL || b_client->notify == NULL) {
790 return 0;
791
792 } else if (a_client->notify == b_client->notify) {
793 return 0;
794
795 } else if (((long)a_client->notify) < ((long)b_client->notify)) {
796 crm_err("callbacks for %s are not equal: %p vs. %p",
797 a_client->event, a_client->notify, b_client->notify);
798 return -1;
799 }
800 crm_err("callbacks for %s are not equal: %p vs. %p",
801 a_client->event, a_client->notify, b_client->notify);
802 return 1;
803 }
804 return rc;
805 }
806
807 xmlNode *
808 stonith_create_op(int call_id, const char *token, const char *op, xmlNode * data, int call_options)
809 {
810 xmlNode *op_msg = NULL;
811
812 CRM_CHECK(token != NULL, return NULL);
813
814 op_msg = pcmk__xe_create(NULL, PCMK__XE_STONITH_COMMAND);
815 crm_xml_add(op_msg, PCMK__XA_T, PCMK__VALUE_STONITH_NG);
816 crm_xml_add(op_msg, PCMK__XA_ST_OP, op);
817 crm_xml_add_int(op_msg, PCMK__XA_ST_CALLID, call_id);
818 crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options);
819 crm_xml_add_int(op_msg, PCMK__XA_ST_CALLOPT, call_options);
820
821 if (data != NULL) {
822 xmlNode *wrapper = pcmk__xe_create(op_msg, PCMK__XE_ST_CALLDATA);
823
824 pcmk__xml_copy(wrapper, data);
825 }
826
827 return op_msg;
828 }
829
830 static void
831 stonith_destroy_op_callback(gpointer data)
832 {
833 stonith_callback_client_t *blob = data;
834
835 if (blob->timer && blob->timer->ref > 0) {
836 g_source_remove(blob->timer->ref);
837 }
838 free(blob->timer);
839 free(blob);
840 }
841
842 static int
843 stonith_api_signoff(stonith_t * stonith)
844 {
845 stonith_private_t *native = stonith->st_private;
846
847 crm_debug("Disconnecting from the fencer");
848
849 if (native->source != NULL) {
850 /* Attached to mainloop */
851 mainloop_del_ipc_client(native->source);
852 native->source = NULL;
853 native->ipc = NULL;
854
855 } else if (native->ipc) {
856 /* Not attached to mainloop */
857 crm_ipc_t *ipc = native->ipc;
858
859 native->ipc = NULL;
860 crm_ipc_close(ipc);
861 crm_ipc_destroy(ipc);
862 }
863
864 free(native->token); native->token = NULL;
865 stonith->state = stonith_disconnected;
866 return pcmk_ok;
867 }
868
869 static int
870 stonith_api_del_callback(stonith_t * stonith, int call_id, bool all_callbacks)
871 {
872 stonith_private_t *private = stonith->st_private;
873
874 if (all_callbacks) {
875 private->op_callback = NULL;
876 g_hash_table_destroy(private->stonith_op_callback_table);
877 private->stonith_op_callback_table = pcmk__intkey_table(stonith_destroy_op_callback);
878
879 } else if (call_id == 0) {
880 private->op_callback = NULL;
881
882 } else {
883 pcmk__intkey_table_remove(private->stonith_op_callback_table, call_id);
884 }
885 return pcmk_ok;
886 }
887
888 /*!
889 * \internal
890 * \brief Invoke a (single) specified fence action callback
891 *
892 * \param[in,out] st Fencer API connection
893 * \param[in] call_id If positive, call ID of completed fence action,
894 * otherwise legacy return code for early failure
895 * \param[in,out] result Full result for action
896 * \param[in,out] userdata User data to pass to callback
897 * \param[in] callback Fence action callback to invoke
898 */
899 static void
900 invoke_fence_action_callback(stonith_t *st, int call_id,
901 pcmk__action_result_t *result,
902 void *userdata,
903 void (*callback) (stonith_t *st,
904 stonith_callback_data_t *data))
905 {
906 stonith_callback_data_t data = { 0, };
907
908 data.call_id = call_id;
909 data.rc = pcmk_rc2legacy(stonith__result2rc(result));
910 data.userdata = userdata;
911 data.opaque = (void *) result;
912
913 callback(st, &data);
914 }
915
916 /*!
917 * \internal
918 * \brief Invoke any callbacks registered for a specified fence action result
919 *
920 * Given a fence action result from the fencer, invoke any callback registered
921 * for that action, as well as any global callback registered.
922 *
923 * \param[in,out] stonith Fencer API connection
924 * \param[in] msg If non-NULL, fencer reply
925 * \param[in] call_id If \p msg is NULL, call ID of action that timed out
926 */
927 static void
928 invoke_registered_callbacks(stonith_t *stonith, const xmlNode *msg, int call_id)
929 {
930 stonith_private_t *private = NULL;
931 stonith_callback_client_t *cb_info = NULL;
932 pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
933
934 CRM_CHECK(stonith != NULL, return);
935 CRM_CHECK(stonith->st_private != NULL, return);
936
937 private = stonith->st_private;
938
939 if (msg == NULL) {
940 // Fencer didn't reply in time
941 pcmk__set_result(&result, CRM_EX_ERROR, PCMK_EXEC_TIMEOUT,
942 "Fencer accepted request but did not reply in time");
943 CRM_LOG_ASSERT(call_id > 0);
944
945 } else {
946 // We have the fencer reply
947 if ((crm_element_value_int(msg, PCMK__XA_ST_CALLID, &call_id) != 0)
948 || (call_id <= 0)) {
949 crm_log_xml_warn(msg, "Bad fencer reply");
950 }
951 stonith__xe_get_result(msg, &result);
952 }
953
954 if (call_id > 0) {
955 cb_info = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
956 call_id);
957 }
958
959 if ((cb_info != NULL) && (cb_info->callback != NULL)
960 && (pcmk__result_ok(&result) || !(cb_info->only_success))) {
961 crm_trace("Invoking callback %s for call %d",
962 pcmk__s(cb_info->id, "without ID"), call_id);
963 invoke_fence_action_callback(stonith, call_id, &result,
964 cb_info->user_data, cb_info->callback);
965
966 } else if ((private->op_callback == NULL) && !pcmk__result_ok(&result)) {
967 crm_warn("Fencing action without registered callback failed: %d (%s%s%s)",
968 result.exit_status,
969 pcmk_exec_status_str(result.execution_status),
970 ((result.exit_reason == NULL)? "" : ": "),
971 ((result.exit_reason == NULL)? "" : result.exit_reason));
972 crm_log_xml_debug(msg, "Failed fence update");
973 }
974
975 if (private->op_callback != NULL) {
976 crm_trace("Invoking global callback for call %d", call_id);
977 invoke_fence_action_callback(stonith, call_id, &result, NULL,
978 private->op_callback);
979 }
980
981 if (cb_info != NULL) {
982 stonith_api_del_callback(stonith, call_id, FALSE);
983 }
984 pcmk__reset_result(&result);
985 }
986
987 static gboolean
988 stonith_async_timeout_handler(gpointer data)
989 {
990 struct timer_rec_s *timer = data;
991
992 crm_err("Async call %d timed out after %dms", timer->call_id, timer->timeout);
993 invoke_registered_callbacks(timer->stonith, NULL, timer->call_id);
994
995 /* Always return TRUE, never remove the handler
996 * We do that in stonith_del_callback()
997 */
998 return TRUE;
999 }
1000
1001 static void
1002 set_callback_timeout(stonith_callback_client_t * callback, stonith_t * stonith, int call_id,
1003 int timeout)
1004 {
1005 struct timer_rec_s *async_timer = callback->timer;
1006
1007 if (timeout <= 0) {
1008 return;
1009 }
1010
1011 if (!async_timer) {
1012 async_timer = pcmk__assert_alloc(1, sizeof(struct timer_rec_s));
1013 callback->timer = async_timer;
1014 }
1015
1016 async_timer->stonith = stonith;
1017 async_timer->call_id = call_id;
1018 /* Allow a fair bit of grace to allow the server to tell us of a timeout
1019 * This is only a fallback
1020 */
1021 async_timer->timeout = (timeout + 60) * 1000;
1022 if (async_timer->ref) {
1023 g_source_remove(async_timer->ref);
1024 }
1025 async_timer->ref =
1026 pcmk__create_timer(async_timer->timeout, stonith_async_timeout_handler,
1027 async_timer);
1028 }
1029
1030 static void
1031 update_callback_timeout(int call_id, int timeout, stonith_t * st)
1032 {
1033 stonith_callback_client_t *callback = NULL;
1034 stonith_private_t *private = st->st_private;
1035
1036 callback = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
1037 call_id);
1038 if (!callback || !callback->allow_timeout_updates) {
1039 return;
1040 }
1041
1042 set_callback_timeout(callback, st, call_id, timeout);
1043 }
1044
1045 static int
1046 stonith_dispatch_internal(const char *buffer, ssize_t length, gpointer userdata)
1047 {
1048 const char *type = NULL;
1049 struct notify_blob_s blob;
1050
1051 stonith_t *st = userdata;
1052 stonith_private_t *private = NULL;
1053
1054 pcmk__assert(st != NULL);
1055 private = st->st_private;
1056
1057 blob.stonith = st;
1058 blob.xml = pcmk__xml_parse(buffer);
1059 if (blob.xml == NULL) {
1060 crm_warn("Received malformed message from fencer: %s", buffer);
1061 return 0;
1062 }
1063
1064 /* do callbacks */
1065 type = crm_element_value(blob.xml, PCMK__XA_T);
1066 crm_trace("Activating %s callbacks...", type);
1067
1068 if (pcmk__str_eq(type, PCMK__VALUE_STONITH_NG, pcmk__str_none)) {
1069 invoke_registered_callbacks(st, blob.xml, 0);
1070
1071 } else if (pcmk__str_eq(type, PCMK__VALUE_ST_NOTIFY, pcmk__str_none)) {
1072 foreach_notify_entry(private, stonith_send_notification, &blob);
1073
1074 } else if (pcmk__str_eq(type, PCMK__VALUE_ST_ASYNC_TIMEOUT_VALUE,
1075 pcmk__str_none)) {
1076 int call_id = 0;
1077 int timeout = 0;
1078
1079 crm_element_value_int(blob.xml, PCMK__XA_ST_TIMEOUT, &timeout);
1080 crm_element_value_int(blob.xml, PCMK__XA_ST_CALLID, &call_id);
1081
1082 update_callback_timeout(call_id, timeout, st);
1083 } else {
1084 crm_err("Unknown message type: %s", type);
1085 crm_log_xml_warn(blob.xml, "BadReply");
1086 }
1087
1088 pcmk__xml_free(blob.xml);
1089 return 1;
1090 }
1091
1092 static int
1093 stonith_api_signon(stonith_t * stonith, const char *name, int *stonith_fd)
1094 {
1095 int rc = pcmk_ok;
1096 stonith_private_t *native = NULL;
1097 const char *display_name = name? name : "client";
1098
1099 struct ipc_client_callbacks st_callbacks = {
1100 .dispatch = stonith_dispatch_internal,
1101 .destroy = stonith_connection_destroy
1102 };
1103
1104 CRM_CHECK(stonith != NULL, return -EINVAL);
1105
1106 native = stonith->st_private;
1107 pcmk__assert(native != NULL);
1108
1109 crm_debug("Attempting fencer connection by %s with%s mainloop",
1110 display_name, (stonith_fd? "out" : ""));
1111
1112 stonith->state = stonith_connected_command;
1113 if (stonith_fd) {
1114 /* No mainloop */
1115 native->ipc = crm_ipc_new("stonith-ng", 0);
1116 if (native->ipc != NULL) {
1117 rc = pcmk__connect_generic_ipc(native->ipc);
1118 if (rc == pcmk_rc_ok) {
1119 rc = pcmk__ipc_fd(native->ipc, stonith_fd);
1120 if (rc != pcmk_rc_ok) {
1121 crm_debug("Couldn't get file descriptor for IPC: %s",
1122 pcmk_rc_str(rc));
1123 }
1124 }
1125 if (rc != pcmk_rc_ok) {
1126 crm_ipc_close(native->ipc);
1127 crm_ipc_destroy(native->ipc);
1128 native->ipc = NULL;
1129 }
1130 }
1131
1132 } else {
1133 /* With mainloop */
1134 native->source =
1135 mainloop_add_ipc_client("stonith-ng", G_PRIORITY_MEDIUM, 0, stonith, &st_callbacks);
1136 native->ipc = mainloop_get_ipc_client(native->source);
1137 }
1138
1139 if (native->ipc == NULL) {
1140 rc = -ENOTCONN;
1141 } else {
1142 xmlNode *reply = NULL;
1143 xmlNode *hello = pcmk__xe_create(NULL, PCMK__XE_STONITH_COMMAND);
1144
1145 crm_xml_add(hello, PCMK__XA_T, PCMK__VALUE_STONITH_NG);
1146 crm_xml_add(hello, PCMK__XA_ST_OP, CRM_OP_REGISTER);
1147 crm_xml_add(hello, PCMK__XA_ST_CLIENTNAME, name);
1148 rc = crm_ipc_send(native->ipc, hello, crm_ipc_client_response, -1, &reply);
1149
1150 if (rc < 0) {
1151 crm_debug("Couldn't register with the fencer: %s "
1152 QB_XS " rc=%d", pcmk_strerror(rc), rc);
1153 rc = -ECOMM;
1154
1155 } else if (reply == NULL) {
1156 crm_debug("Couldn't register with the fencer: no reply");
1157 rc = -EPROTO;
1158
1159 } else {
1160 const char *msg_type = crm_element_value(reply, PCMK__XA_ST_OP);
1161
1162 native->token = crm_element_value_copy(reply, PCMK__XA_ST_CLIENTID);
1163 if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_none)) {
1164 crm_debug("Couldn't register with the fencer: invalid reply type '%s'",
1165 (msg_type? msg_type : "(missing)"));
1166 crm_log_xml_debug(reply, "Invalid fencer reply");
1167 rc = -EPROTO;
1168
1169 } else if (native->token == NULL) {
1170 crm_debug("Couldn't register with the fencer: no token in reply");
1171 crm_log_xml_debug(reply, "Invalid fencer reply");
1172 rc = -EPROTO;
1173
1174 } else {
1175 crm_debug("Connection to fencer by %s succeeded (registration token: %s)",
1176 display_name, native->token);
1177 rc = pcmk_ok;
1178 }
1179 }
1180
1181 pcmk__xml_free(reply);
1182 pcmk__xml_free(hello);
1183 }
1184
1185 if (rc != pcmk_ok) {
1186 crm_debug("Connection attempt to fencer by %s failed: %s "
1187 QB_XS " rc=%d", display_name, pcmk_strerror(rc), rc);
1188 stonith->cmds->disconnect(stonith);
1189 }
1190 return rc;
1191 }
1192
1193 static int
1194 stonith_set_notification(stonith_t * stonith, const char *callback, int enabled)
1195 {
1196 int rc = pcmk_ok;
1197 xmlNode *notify_msg = pcmk__xe_create(NULL, __func__);
1198 stonith_private_t *native = stonith->st_private;
1199
1200 if (stonith->state != stonith_disconnected) {
1201
1202 crm_xml_add(notify_msg, PCMK__XA_ST_OP, STONITH_OP_NOTIFY);
1203 if (enabled) {
1204 crm_xml_add(notify_msg, PCMK__XA_ST_NOTIFY_ACTIVATE, callback);
1205 } else {
1206 crm_xml_add(notify_msg, PCMK__XA_ST_NOTIFY_DEACTIVATE, callback);
1207 }
1208
1209 rc = crm_ipc_send(native->ipc, notify_msg, crm_ipc_client_response, -1, NULL);
1210 if (rc < 0) {
1211 crm_perror(LOG_DEBUG, "Couldn't register for fencing notifications: %d", rc);
1212 rc = -ECOMM;
1213 } else {
1214 rc = pcmk_ok;
1215 }
1216 }
1217
1218 pcmk__xml_free(notify_msg);
1219 return rc;
1220 }
1221
1222 static int
1223 stonith_api_add_notification(stonith_t * stonith, const char *event,
1224 void (*callback) (stonith_t * stonith, stonith_event_t * e))
1225 {
1226 GList *list_item = NULL;
1227 stonith_notify_client_t *new_client = NULL;
1228 stonith_private_t *private = NULL;
1229
1230 private = stonith->st_private;
1231 crm_trace("Adding callback for %s events (%d)", event, g_list_length(private->notify_list));
1232
1233 new_client = pcmk__assert_alloc(1, sizeof(stonith_notify_client_t));
1234 new_client->event = event;
1235 new_client->notify = callback;
1236
1237 list_item = g_list_find_custom(private->notify_list, new_client, stonithlib_GCompareFunc);
1238
1239 if (list_item != NULL) {
1240 crm_warn("Callback already present");
1241 free(new_client);
1242 return -ENOTUNIQ;
1243
1244 } else {
1245 private->notify_list = g_list_append(private->notify_list, new_client);
1246
1247 stonith_set_notification(stonith, event, 1);
1248
1249 crm_trace("Callback added (%d)", g_list_length(private->notify_list));
1250 }
1251 return pcmk_ok;
1252 }
1253
1254 static void
1255 del_notify_entry(gpointer data, gpointer user_data)
1256 {
1257 stonith_notify_client_t *entry = data;
1258 stonith_t * stonith = user_data;
1259
1260 if (!entry->delete) {
1261 crm_debug("Removing callback for %s events", entry->event);
1262 stonith_api_del_notification(stonith, entry->event);
1263 }
1264 }
1265
1266 static int
1267 stonith_api_del_notification(stonith_t * stonith, const char *event)
1268 {
1269 GList *list_item = NULL;
1270 stonith_notify_client_t *new_client = NULL;
1271 stonith_private_t *private = stonith->st_private;
1272
1273 if (event == NULL) {
1274 foreach_notify_entry(private, del_notify_entry, stonith);
1275 crm_trace("Removed callback");
1276
1277 return pcmk_ok;
1278 }
1279
1280 crm_debug("Removing callback for %s events", event);
1281
1282 new_client = pcmk__assert_alloc(1, sizeof(stonith_notify_client_t));
1283 new_client->event = event;
1284 new_client->notify = NULL;
1285
1286 list_item = g_list_find_custom(private->notify_list, new_client, stonithlib_GCompareFunc);
1287
1288 stonith_set_notification(stonith, event, 0);
1289
1290 if (list_item != NULL) {
1291 stonith_notify_client_t *list_client = list_item->data;
1292
1293 if (private->notify_refcnt) {
1294 list_client->delete = TRUE;
1295 private->notify_deletes = TRUE;
1296 } else {
1297 private->notify_list = g_list_remove(private->notify_list, list_client);
1298 free(list_client);
1299 }
1300
1301 crm_trace("Removed callback");
1302
1303 } else {
1304 crm_trace("Callback not present");
1305 }
1306 free(new_client);
1307 return pcmk_ok;
1308 }
1309
1310 static int
1311 stonith_api_add_callback(stonith_t * stonith, int call_id, int timeout, int options,
1312 void *user_data, const char *callback_name,
1313 void (*callback) (stonith_t * st, stonith_callback_data_t * data))
1314 {
1315 stonith_callback_client_t *blob = NULL;
1316 stonith_private_t *private = NULL;
1317
1318 CRM_CHECK(stonith != NULL, return -EINVAL);
1319 CRM_CHECK(stonith->st_private != NULL, return -EINVAL);
1320 private = stonith->st_private;
1321
1322 if (call_id == 0) { // Add global callback
1323 private->op_callback = callback;
1324
1325 } else if (call_id < 0) { // Call failed immediately, so call callback now
1326 if (!(options & st_opt_report_only_success)) {
1327 pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
1328
1329 crm_trace("Call failed, calling %s: %s", callback_name, pcmk_strerror(call_id));
1330 pcmk__set_result(&result, CRM_EX_ERROR,
1331 stonith__legacy2status(call_id), NULL);
1332 invoke_fence_action_callback(stonith, call_id, &result,
1333 user_data, callback);
1334 } else {
1335 crm_warn("Fencer call failed: %s", pcmk_strerror(call_id));
1336 }
1337 return FALSE;
1338 }
1339
1340 blob = pcmk__assert_alloc(1, sizeof(stonith_callback_client_t));
1341 blob->id = callback_name;
1342 blob->only_success = (options & st_opt_report_only_success) ? TRUE : FALSE;
1343 blob->user_data = user_data;
1344 blob->callback = callback;
1345 blob->allow_timeout_updates = (options & st_opt_timeout_updates) ? TRUE : FALSE;
1346
1347 if (timeout > 0) {
1348 set_callback_timeout(blob, stonith, call_id, timeout);
1349 }
1350
1351 pcmk__intkey_table_insert(private->stonith_op_callback_table, call_id,
1352 blob);
1353 crm_trace("Added callback to %s for call %d", callback_name, call_id);
1354
1355 return TRUE;
1356 }
1357
1358 static void
1359 stonith_dump_pending_op(gpointer key, gpointer value, gpointer user_data)
1360 {
1361 int call = GPOINTER_TO_INT(key);
1362 stonith_callback_client_t *blob = value;
1363
1364 crm_debug("Call %d (%s): pending", call, pcmk__s(blob->id, "no ID"));
1365 }
1366
1367 void
1368 stonith_dump_pending_callbacks(stonith_t * stonith)
1369 {
1370 stonith_private_t *private = stonith->st_private;
1371
1372 if (private->stonith_op_callback_table == NULL) {
1373 return;
1374 }
1375 return g_hash_table_foreach(private->stonith_op_callback_table, stonith_dump_pending_op, NULL);
1376 }
1377
1378 /*!
1379 * \internal
1380 * \brief Get the data section of a fencer notification
1381 *
1382 * \param[in] msg Notification XML
1383 * \param[in] ntype Notification type
1384 */
1385 static xmlNode *
1386 get_event_data_xml(xmlNode *msg, const char *ntype)
1387 {
1388 char *data_addr = crm_strdup_printf("//%s", ntype);
1389 xmlNode *data = get_xpath_object(data_addr, msg, LOG_DEBUG);
1390
1391 free(data_addr);
1392 return data;
1393 }
1394
1395 /*
1396 <notify t="st_notify" subt="st_device_register" st_op="st_device_register" st_rc="0" >
1397 <st_calldata >
1398 <stonith_command t="stonith-ng" st_async_id="088fb640-431a-48b9-b2fc-c4ff78d0a2d9" st_op="st_device_register" st_callid="2" st_callopt="4096" st_timeout="0" st_clientid="088fb640-431a-48b9-b2fc-c4ff78d0a2d9" st_clientname="cts-fence-helper" >
1399 <st_calldata >
1400 <st_device_id id="test-id" origin="create_device_registration_xml" agent="fence_virsh" namespace="stonith-ng" >
1401 <attributes ipaddr="localhost" pcmk-portmal="some-host=pcmk-1 pcmk-3=3,4" login="root" identity_file="/root/.ssh/id_dsa" />
1402 </st_device_id>
1403 </st_calldata>
1404 </stonith_command>
1405 </st_calldata>
1406 </notify>
1407
1408 <notify t="st_notify" subt="st_notify_fence" st_op="st_notify_fence" st_rc="0" >
1409 <st_calldata >
1410 <st_notify_fence st_rc="0" st_target="some-host" st_op="st_fence" st_delegate="test-id" st_origin="61dd7759-e229-4be7-b1f8-ef49dd14d9f0" />
1411 </st_calldata>
1412 </notify>
1413 */
1414 static stonith_event_t *
1415 xml_to_event(xmlNode *msg)
1416 {
1417 stonith_event_t *event = pcmk__assert_alloc(1, sizeof(stonith_event_t));
1418 struct event_private *event_private = NULL;
1419
1420 event->opaque = pcmk__assert_alloc(1, sizeof(struct event_private));
1421 event_private = (struct event_private *) event->opaque;
1422
1423 crm_log_xml_trace(msg, "stonith_notify");
1424
1425 // All notification types have the operation result and notification subtype
1426 stonith__xe_get_result(msg, &event_private->result);
1427 event->operation = crm_element_value_copy(msg, PCMK__XA_ST_OP);
1428
1429 // @COMPAT The API originally provided the result as a legacy return code
1430 event->result = pcmk_rc2legacy(stonith__result2rc(&event_private->result));
1431
1432 // Some notification subtypes have additional information
1433
1434 if (pcmk__str_eq(event->operation, PCMK__VALUE_ST_NOTIFY_FENCE,
1435 pcmk__str_none)) {
1436 xmlNode *data = get_event_data_xml(msg, event->operation);
1437
1438 if (data == NULL) {
1439 crm_err("No data for %s event", event->operation);
1440 crm_log_xml_notice(msg, "BadEvent");
1441 } else {
1442 event->origin = crm_element_value_copy(data, PCMK__XA_ST_ORIGIN);
1443 event->action = crm_element_value_copy(data,
1444 PCMK__XA_ST_DEVICE_ACTION);
1445 event->target = crm_element_value_copy(data, PCMK__XA_ST_TARGET);
1446 event->executioner = crm_element_value_copy(data,
1447 PCMK__XA_ST_DELEGATE);
1448 event->id = crm_element_value_copy(data, PCMK__XA_ST_REMOTE_OP);
1449 event->client_origin =
1450 crm_element_value_copy(data, PCMK__XA_ST_CLIENTNAME);
1451 event->device = crm_element_value_copy(data, PCMK__XA_ST_DEVICE_ID);
1452 }
1453
1454 } else if (pcmk__str_any_of(event->operation,
1455 STONITH_OP_DEVICE_ADD, STONITH_OP_DEVICE_DEL,
1456 STONITH_OP_LEVEL_ADD, STONITH_OP_LEVEL_DEL,
1457 NULL)) {
1458 xmlNode *data = get_event_data_xml(msg, event->operation);
1459
1460 if (data == NULL) {
1461 crm_err("No data for %s event", event->operation);
1462 crm_log_xml_notice(msg, "BadEvent");
1463 } else {
1464 event->device = crm_element_value_copy(data, PCMK__XA_ST_DEVICE_ID);
1465 }
1466 }
1467
1468 return event;
1469 }
1470
1471 static void
1472 event_free(stonith_event_t * event)
1473 {
1474 struct event_private *event_private = event->opaque;
1475
1476 free(event->id);
1477 free(event->operation);
1478 free(event->origin);
1479 free(event->action);
1480 free(event->target);
1481 free(event->executioner);
1482 free(event->device);
1483 free(event->client_origin);
1484 pcmk__reset_result(&event_private->result);
1485 free(event->opaque);
1486 free(event);
1487 }
1488
1489 static void
1490 stonith_send_notification(gpointer data, gpointer user_data)
1491 {
1492 struct notify_blob_s *blob = user_data;
1493 stonith_notify_client_t *entry = data;
1494 stonith_event_t *st_event = NULL;
1495 const char *event = NULL;
1496
|
(1) Event path: |
Condition "blob->xml == NULL", taking false branch. |
1497 if (blob->xml == NULL) {
1498 crm_warn("Skipping callback - NULL message");
1499 return;
1500 }
1501
1502 event = crm_element_value(blob->xml, PCMK__XA_SUBT);
1503
|
(2) Event path: |
Condition "entry == NULL", taking false branch. |
1504 if (entry == NULL) {
1505 crm_warn("Skipping callback - NULL callback client");
1506 return;
1507
|
(3) Event path: |
Condition "entry->delete", taking false branch. |
1508 } else if (entry->delete) {
1509 crm_trace("Skipping callback - marked for deletion");
1510 return;
1511
|
(4) Event path: |
Condition "entry->notify == NULL", taking false branch. |
|
(12) Event example_checked: |
Example 1: "entry->notify" has its value checked in "entry->notify == NULL". |
| Also see events: |
[null_field][dereference][example_checked] |
1512 } else if (entry->notify == NULL) {
1513 crm_warn("Skipping callback - NULL callback");
1514 return;
1515
|
(5) Event path: |
Condition "!pcmk__str_eq(entry->event, event, pcmk__str_none)", taking false branch. |
1516 } else if (!pcmk__str_eq(entry->event, event, pcmk__str_none)) {
1517 crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event);
1518 return;
1519 }
1520
1521 st_event = xml_to_event(blob->xml);
1522
|
(6) Event path: |
Switch case default. |
|
(7) Event path: |
Condition "trace_cs == NULL", taking true branch. |
|
(8) Event path: |
Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking true branch. |
|
(9) Event path: |
Breaking from switch. |
1523 crm_trace("Invoking callback for %p/%s event...", entry, event);
|
CID (unavailable; MK=f4c0a167a8b0e2b5d87823acecf38287) (#1 of 1): Dereference of potentially null field (NULL_FIELD): |
|
(10) Event null_field: |
Reading field "notify", which is expected to possibly be "NULL" in "entry->notify" (checked 2 out of 2 times). |
|
(11) Event dereference: |
Dereferencing "entry->notify", which is known to be "NULL". |
| Also see events: |
[example_checked][example_checked] |
1524 entry->notify(blob->stonith, st_event);
1525 crm_trace("Callback invoked...");
1526
1527 event_free(st_event);
1528 }
1529
1530 /*!
1531 * \internal
1532 * \brief Create and send an API request
1533 *
1534 * \param[in,out] stonith Stonith connection
1535 * \param[in] op API operation to request
1536 * \param[in] data Data to attach to request
1537 * \param[out] output_data If not NULL, will be set to reply if synchronous
1538 * \param[in] call_options Bitmask of stonith_call_options to use
1539 * \param[in] timeout Error if not completed within this many seconds
1540 *
1541 * \return pcmk_ok (for synchronous requests) or positive call ID
1542 * (for asynchronous requests) on success, -errno otherwise
1543 */
1544 static int
1545 stonith_send_command(stonith_t * stonith, const char *op, xmlNode * data, xmlNode ** output_data,
1546 int call_options, int timeout)
1547 {
1548 int rc = 0;
1549 int reply_id = -1;
1550
1551 xmlNode *op_msg = NULL;
1552 xmlNode *op_reply = NULL;
1553 stonith_private_t *native = NULL;
1554
1555 pcmk__assert((stonith != NULL) && (stonith->st_private != NULL)
1556 && (op != NULL));
1557 native = stonith->st_private;
1558
1559 if (output_data != NULL) {
1560 *output_data = NULL;
1561 }
1562
1563 if ((stonith->state == stonith_disconnected) || (native->token == NULL)) {
1564 return -ENOTCONN;
1565 }
1566
1567 /* Increment the call ID, which must be positive to avoid conflicting with
1568 * error codes. This shouldn't be a problem unless the client mucked with
1569 * it or the counter wrapped around.
1570 */
1571 stonith->call_id++;
1572 if (stonith->call_id < 1) {
1573 stonith->call_id = 1;
1574 }
1575
1576 op_msg = stonith_create_op(stonith->call_id, native->token, op, data, call_options);
1577 if (op_msg == NULL) {
1578 return -EINVAL;
1579 }
1580
1581 crm_xml_add_int(op_msg, PCMK__XA_ST_TIMEOUT, timeout);
1582 crm_trace("Sending %s message to fencer with timeout %ds", op, timeout);
1583
1584 if (data) {
1585 const char *delay_s = crm_element_value(data, PCMK__XA_ST_DELAY);
1586
1587 if (delay_s) {
1588 crm_xml_add(op_msg, PCMK__XA_ST_DELAY, delay_s);
1589 }
1590 }
1591
1592 {
1593 enum crm_ipc_flags ipc_flags = crm_ipc_flags_none;
1594
1595 if (call_options & st_opt_sync_call) {
1596 pcmk__set_ipc_flags(ipc_flags, "stonith command",
1597 crm_ipc_client_response);
1598 }
1599 rc = crm_ipc_send(native->ipc, op_msg, ipc_flags,
1600 1000 * (timeout + 60), &op_reply);
1601 }
1602 pcmk__xml_free(op_msg);
1603
1604 if (rc < 0) {
1605 crm_perror(LOG_ERR, "Couldn't perform %s operation (timeout=%ds): %d", op, timeout, rc);
1606 rc = -ECOMM;
1607 goto done;
1608 }
1609
1610 crm_log_xml_trace(op_reply, "Reply");
1611
1612 if (!(call_options & st_opt_sync_call)) {
1613 crm_trace("Async call %d, returning", stonith->call_id);
1614 pcmk__xml_free(op_reply);
1615 return stonith->call_id;
1616 }
1617
1618 crm_element_value_int(op_reply, PCMK__XA_ST_CALLID, &reply_id);
1619
1620 if (reply_id == stonith->call_id) {
1621 pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
1622
1623 crm_trace("Synchronous reply %d received", reply_id);
1624
1625 stonith__xe_get_result(op_reply, &result);
1626 rc = pcmk_rc2legacy(stonith__result2rc(&result));
1627 pcmk__reset_result(&result);
1628
1629 if ((call_options & st_opt_discard_reply) || output_data == NULL) {
1630 crm_trace("Discarding reply");
1631
1632 } else {
1633 *output_data = op_reply;
1634 op_reply = NULL; /* Prevent subsequent free */
1635 }
1636
1637 } else if (reply_id <= 0) {
1638 crm_err("Received bad reply: No id set");
1639 crm_log_xml_err(op_reply, "Bad reply");
1640 pcmk__xml_free(op_reply);
1641 op_reply = NULL;
1642 rc = -ENOMSG;
1643
1644 } else {
1645 crm_err("Received bad reply: %d (wanted %d)", reply_id, stonith->call_id);
1646 crm_log_xml_err(op_reply, "Old reply");
1647 pcmk__xml_free(op_reply);
1648 op_reply = NULL;
1649 rc = -ENOMSG;
1650 }
1651
1652 done:
1653 if (!crm_ipc_connected(native->ipc)) {
1654 crm_err("Fencer disconnected");
1655 free(native->token); native->token = NULL;
1656 stonith->state = stonith_disconnected;
1657 }
1658
1659 pcmk__xml_free(op_reply);
1660 return rc;
1661 }
1662
1663 /* Not used with mainloop */
1664 bool
1665 stonith_dispatch(stonith_t * st)
1666 {
1667 gboolean stay_connected = TRUE;
1668 stonith_private_t *private = NULL;
1669
1670 pcmk__assert(st != NULL);
1671 private = st->st_private;
1672
1673 while (crm_ipc_ready(private->ipc)) {
1674
1675 if (crm_ipc_read(private->ipc) > 0) {
1676 const char *msg = crm_ipc_buffer(private->ipc);
1677
1678 stonith_dispatch_internal(msg, strlen(msg), st);
1679 }
1680
1681 if (!crm_ipc_connected(private->ipc)) {
1682 crm_err("Connection closed");
1683 stay_connected = FALSE;
1684 }
1685 }
1686
1687 return stay_connected;
1688 }
1689
1690 static int
1691 stonith_api_free(stonith_t * stonith)
1692 {
1693 int rc = pcmk_ok;
1694
1695 crm_trace("Destroying %p", stonith);
1696
1697 if (stonith->state != stonith_disconnected) {
1698 crm_trace("Unregistering notifications and disconnecting %p first",
1699 stonith);
1700 stonith->cmds->remove_notification(stonith, NULL);
1701 rc = stonith->cmds->disconnect(stonith);
1702 }
1703
1704 if (stonith->state == stonith_disconnected) {
1705 stonith_private_t *private = stonith->st_private;
1706
1707 crm_trace("Removing %d callbacks", g_hash_table_size(private->stonith_op_callback_table));
1708 g_hash_table_destroy(private->stonith_op_callback_table);
1709
1710 crm_trace("Destroying %d notification clients", g_list_length(private->notify_list));
1711 g_list_free_full(private->notify_list, free);
1712
1713 free(stonith->st_private);
1714 free(stonith->cmds);
1715 free(stonith);
1716
1717 } else {
1718 crm_err("Not free'ing active connection: %s (%d)", pcmk_strerror(rc), rc);
1719 }
1720
1721 return rc;
1722 }
1723
1724 void
1725 stonith_api_delete(stonith_t * stonith)
1726 {
1727 crm_trace("Destroying %p", stonith);
1728 if(stonith) {
1729 stonith->cmds->free(stonith);
1730 }
1731 }
1732
1733 static gboolean
1734 is_stonith_param(gpointer key, gpointer value, gpointer user_data)
1735 {
1736 return pcmk_stonith_param(key);
1737 }
1738
1739 int
1740 stonith__validate(stonith_t *st, int call_options, const char *rsc_id,
1741 const char *namespace_s, const char *agent,
1742 GHashTable *params, int timeout_sec, char **output,
1743 char **error_output)
1744 {
1745 int rc = pcmk_rc_ok;
1746
1747 /* Use a dummy node name in case the agent requires a target. We assume the
1748 * actual target doesn't matter for validation purposes (if in practice,
1749 * that is incorrect, we will need to allow the caller to pass the target).
1750 */
1751 const char *target = "node1";
1752 char *host_arg = NULL;
1753
1754 if (params != NULL) {
1755 host_arg = pcmk__str_copy(g_hash_table_lookup(params, PCMK_STONITH_HOST_ARGUMENT));
1756
1757 /* Remove special stonith params from the table before doing anything else */
1758 g_hash_table_foreach_remove(params, is_stonith_param, NULL);
1759 }
1760
1761 #if PCMK__ENABLE_CIBSECRETS
1762 rc = pcmk__substitute_secrets(rsc_id, params);
1763 if (rc != pcmk_rc_ok) {
1764 crm_warn("Could not replace secret parameters for validation of %s: %s",
1765 agent, pcmk_rc_str(rc));
1766 // rc is standard return value, don't return it in this function
1767 }
1768 #endif
1769
1770 if (output) {
1771 *output = NULL;
1772 }
1773 if (error_output) {
1774 *error_output = NULL;
1775 }
1776
1777 if (timeout_sec <= 0) {
1778 timeout_sec = PCMK_DEFAULT_ACTION_TIMEOUT_MS;
1779 }
1780
1781 switch (stonith_get_namespace(agent, namespace_s)) {
1782 case st_namespace_rhcs:
1783 rc = stonith__rhcs_validate(st, call_options, target, agent,
1784 params, host_arg, timeout_sec,
1785 output, error_output);
1786 rc = pcmk_legacy2rc(rc);
1787 break;
1788
1789 #if HAVE_STONITH_STONITH_H
1790 case st_namespace_lha:
1791 rc = stonith__lha_validate(st, call_options, target, agent,
1792 params, timeout_sec, output,
1793 error_output);
1794 rc = pcmk_legacy2rc(rc);
1795 break;
1796 #endif
1797
1798 case st_namespace_invalid:
1799 errno = ENOENT;
1800 rc = errno;
1801
1802 if (error_output) {
1803 *error_output = crm_strdup_printf("Agent %s not found", agent);
1804 } else {
1805 crm_err("Agent %s not found", agent);
1806 }
1807
1808 break;
1809
1810 default:
1811 errno = EOPNOTSUPP;
1812 rc = errno;
1813
1814 if (error_output) {
1815 *error_output = crm_strdup_printf("Agent %s does not support validation",
1816 agent);
1817 } else {
1818 crm_err("Agent %s does not support validation", agent);
1819 }
1820
1821 break;
1822 }
1823
1824 free(host_arg);
1825 return rc;
1826 }
1827
1828 static int
1829 stonith_api_validate(stonith_t *st, int call_options, const char *rsc_id,
1830 const char *namespace_s, const char *agent,
1831 const stonith_key_value_t *params, int timeout_sec,
1832 char **output, char **error_output)
1833 {
1834 /* Validation should be done directly via the agent, so we can get it from
1835 * stonith_admin when the cluster is not running, which is important for
1836 * higher-level tools.
1837 */
1838
1839 int rc = pcmk_ok;
1840
1841 GHashTable *params_table = pcmk__strkey_table(free, free);
1842
1843 // Convert parameter list to a hash table
1844 for (; params; params = params->next) {
1845 if (!pcmk_stonith_param(params->key)) {
1846 pcmk__insert_dup(params_table, params->key, params->value);
1847 }
1848 }
1849
1850 rc = stonith__validate(st, call_options, rsc_id, namespace_s, agent,
1851 params_table, timeout_sec, output, error_output);
1852
1853 g_hash_table_destroy(params_table);
1854 return rc;
1855 }
1856
1857 stonith_t *
1858 stonith_api_new(void)
1859 {
1860 stonith_t *new_stonith = NULL;
1861 stonith_private_t *private = NULL;
1862
1863 new_stonith = calloc(1, sizeof(stonith_t));
1864 if (new_stonith == NULL) {
1865 return NULL;
1866 }
1867
1868 private = calloc(1, sizeof(stonith_private_t));
1869 if (private == NULL) {
1870 free(new_stonith);
1871 return NULL;
1872 }
1873 new_stonith->st_private = private;
1874
1875 private->stonith_op_callback_table = pcmk__intkey_table(stonith_destroy_op_callback);
1876 private->notify_list = NULL;
1877 private->notify_refcnt = 0;
1878 private->notify_deletes = FALSE;
1879
1880 new_stonith->call_id = 1;
1881 new_stonith->state = stonith_disconnected;
1882
1883 new_stonith->cmds = calloc(1, sizeof(stonith_api_operations_t));
1884 if (new_stonith->cmds == NULL) {
1885 free(new_stonith->st_private);
1886 free(new_stonith);
1887 return NULL;
1888 }
1889
1890 /* *INDENT-OFF* */
1891 new_stonith->cmds->free = stonith_api_free;
1892 new_stonith->cmds->connect = stonith_api_signon;
1893 new_stonith->cmds->disconnect = stonith_api_signoff;
1894
1895 new_stonith->cmds->list = stonith_api_list;
1896 new_stonith->cmds->monitor = stonith_api_monitor;
1897 new_stonith->cmds->status = stonith_api_status;
1898 new_stonith->cmds->fence = stonith_api_fence;
1899 new_stonith->cmds->fence_with_delay = stonith_api_fence_with_delay;
1900 new_stonith->cmds->confirm = stonith_api_confirm;
1901 new_stonith->cmds->history = stonith_api_history;
1902
1903 new_stonith->cmds->list_agents = stonith_api_device_list;
1904 new_stonith->cmds->metadata = stonith_api_device_metadata;
1905
1906 new_stonith->cmds->query = stonith_api_query;
1907 new_stonith->cmds->remove_device = stonith_api_remove_device;
1908 new_stonith->cmds->register_device = stonith_api_register_device;
1909
1910 new_stonith->cmds->remove_level = stonith_api_remove_level;
1911 new_stonith->cmds->remove_level_full = stonith_api_remove_level_full;
1912 new_stonith->cmds->register_level = stonith_api_register_level;
1913 new_stonith->cmds->register_level_full = stonith_api_register_level_full;
1914
1915 new_stonith->cmds->remove_callback = stonith_api_del_callback;
1916 new_stonith->cmds->register_callback = stonith_api_add_callback;
1917 new_stonith->cmds->remove_notification = stonith_api_del_notification;
1918 new_stonith->cmds->register_notification = stonith_api_add_notification;
1919
1920 new_stonith->cmds->validate = stonith_api_validate;
1921 /* *INDENT-ON* */
1922
1923 return new_stonith;
1924 }
1925
1926 /*!
1927 * \brief Make a blocking connection attempt to the fencer
1928 *
1929 * \param[in,out] st Fencer API object
1930 * \param[in] name Client name to use with fencer
1931 * \param[in] max_attempts Return error if this many attempts fail
1932 *
1933 * \return pcmk_ok on success, result of last attempt otherwise
1934 */
1935 int
1936 stonith_api_connect_retry(stonith_t *st, const char *name, int max_attempts)
1937 {
1938 int rc = -EINVAL; // if max_attempts is not positive
1939
1940 for (int attempt = 1; attempt <= max_attempts; attempt++) {
1941 rc = st->cmds->connect(st, name, NULL);
1942 if (rc == pcmk_ok) {
1943 return pcmk_ok;
1944 } else if (attempt < max_attempts) {
1945 crm_notice("Fencer connection attempt %d of %d failed (retrying in 2s): %s "
1946 QB_XS " rc=%d",
1947 attempt, max_attempts, pcmk_strerror(rc), rc);
1948 sleep(2);
1949 }
1950 }
1951 crm_notice("Could not connect to fencer: %s " QB_XS " rc=%d",
1952 pcmk_strerror(rc), rc);
1953 return rc;
1954 }
1955
1956 stonith_key_value_t *
1957 stonith_key_value_add(stonith_key_value_t * head, const char *key, const char *value)
1958 {
1959 stonith_key_value_t *p, *end;
1960
1961 p = pcmk__assert_alloc(1, sizeof(stonith_key_value_t));
1962 p->key = pcmk__str_copy(key);
1963 p->value = pcmk__str_copy(value);
1964
1965 end = head;
1966 while (end && end->next) {
1967 end = end->next;
1968 }
1969
1970 if (end) {
1971 end->next = p;
1972 } else {
1973 head = p;
1974 }
1975
1976 return head;
1977 }
1978
1979 void
1980 stonith_key_value_freeall(stonith_key_value_t * head, int keys, int values)
1981 {
1982 stonith_key_value_t *p;
1983
1984 while (head) {
1985 p = head->next;
1986 if (keys) {
1987 free(head->key);
1988 }
1989 if (values) {
1990 free(head->value);
1991 }
1992 free(head);
1993 head = p;
1994 }
1995 }
1996
1997 #define api_log_open() openlog("stonith-api", LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON)
1998 #define api_log(level, fmt, args...) syslog(level, "%s: "fmt, __func__, args)
1999
2000 int
2001 stonith_api_kick(uint32_t nodeid, const char *uname, int timeout, bool off)
2002 {
2003 int rc = pcmk_ok;
2004 stonith_t *st = stonith_api_new();
2005 const char *action = off? PCMK_ACTION_OFF : PCMK_ACTION_REBOOT;
2006
2007 api_log_open();
2008 if (st == NULL) {
2009 api_log(LOG_ERR, "API initialization failed, could not kick (%s) node %u/%s",
2010 action, nodeid, uname);
2011 return -EPROTO;
2012 }
2013
2014 rc = st->cmds->connect(st, "stonith-api", NULL);
2015 if (rc != pcmk_ok) {
2016 api_log(LOG_ERR, "Connection failed, could not kick (%s) node %u/%s : %s (%d)",
2017 action, nodeid, uname, pcmk_strerror(rc), rc);
2018 } else {
2019 char *name = (uname == NULL)? pcmk__itoa(nodeid) : strdup(uname);
2020 int opts = 0;
2021
2022 stonith__set_call_options(opts, name,
2023 st_opt_sync_call|st_opt_allow_self_fencing);
2024 if ((uname == NULL) && (nodeid > 0)) {
2025 stonith__set_call_options(opts, name, st_opt_cs_nodeid);
2026 }
2027 rc = st->cmds->fence(st, opts, name, action, timeout, 0);
2028 free(name);
2029
2030 if (rc != pcmk_ok) {
2031 api_log(LOG_ERR, "Could not kick (%s) node %u/%s : %s (%d)",
2032 action, nodeid, uname, pcmk_strerror(rc), rc);
2033 } else {
2034 api_log(LOG_NOTICE, "Node %u/%s kicked: %s", nodeid, uname, action);
2035 }
2036 }
2037
2038 stonith_api_delete(st);
2039 return rc;
2040 }
2041
2042 time_t
2043 stonith_api_time(uint32_t nodeid, const char *uname, bool in_progress)
2044 {
2045 int rc = pcmk_ok;
2046 time_t when = 0;
2047 stonith_t *st = stonith_api_new();
2048 stonith_history_t *history = NULL, *hp = NULL;
2049
2050 if (st == NULL) {
2051 api_log(LOG_ERR, "Could not retrieve fence history for %u/%s: "
2052 "API initialization failed", nodeid, uname);
2053 return when;
2054 }
2055
2056 rc = st->cmds->connect(st, "stonith-api", NULL);
2057 if (rc != pcmk_ok) {
2058 api_log(LOG_NOTICE, "Connection failed: %s (%d)", pcmk_strerror(rc), rc);
2059 } else {
2060 int entries = 0;
2061 int progress = 0;
2062 int completed = 0;
2063 int opts = 0;
2064 char *name = (uname == NULL)? pcmk__itoa(nodeid) : strdup(uname);
2065
2066 stonith__set_call_options(opts, name, st_opt_sync_call);
2067 if ((uname == NULL) && (nodeid > 0)) {
2068 stonith__set_call_options(opts, name, st_opt_cs_nodeid);
2069 }
2070 rc = st->cmds->history(st, opts, name, &history, 120);
2071 free(name);
2072
2073 for (hp = history; hp; hp = hp->next) {
2074 entries++;
2075 if (in_progress) {
2076 progress++;
2077 if (hp->state != st_done && hp->state != st_failed) {
2078 when = time(NULL);
2079 }
2080
2081 } else if (hp->state == st_done) {
2082 completed++;
2083 if (hp->completed > when) {
2084 when = hp->completed;
2085 }
2086 }
2087 }
2088
2089 stonith_history_free(history);
2090
2091 if(rc == pcmk_ok) {
2092 api_log(LOG_INFO, "Found %d entries for %u/%s: %d in progress, %d completed", entries, nodeid, uname, progress, completed);
2093 } else {
2094 api_log(LOG_ERR, "Could not retrieve fence history for %u/%s: %s (%d)", nodeid, uname, pcmk_strerror(rc), rc);
2095 }
2096 }
2097
2098 stonith_api_delete(st);
2099
2100 if(when) {
2101 api_log(LOG_INFO, "Node %u/%s last kicked at: %ld", nodeid, uname, (long int)when);
2102 }
2103 return when;
2104 }
2105
2106 bool
2107 stonith_agent_exists(const char *agent, int timeout)
2108 {
2109 stonith_t *st = NULL;
2110 stonith_key_value_t *devices = NULL;
2111 stonith_key_value_t *dIter = NULL;
2112 bool rc = FALSE;
2113
2114 if (agent == NULL) {
2115 return rc;
2116 }
2117
2118 st = stonith_api_new();
2119 if (st == NULL) {
2120 crm_err("Could not list fence agents: API memory allocation failed");
2121 return FALSE;
2122 }
2123 st->cmds->list_agents(st, st_opt_sync_call, NULL, &devices, timeout == 0 ? 120 : timeout);
2124
2125 for (dIter = devices; dIter != NULL; dIter = dIter->next) {
2126 if (pcmk__str_eq(dIter->value, agent, pcmk__str_none)) {
2127 rc = TRUE;
2128 break;
2129 }
2130 }
2131
2132 stonith_key_value_freeall(devices, 1, 1);
2133 stonith_api_delete(st);
2134 return rc;
2135 }
2136
2137 const char *
2138 stonith_action_str(const char *action)
2139 {
2140 if (action == NULL) {
2141 return "fencing";
2142 } else if (strcmp(action, PCMK_ACTION_ON) == 0) {
2143 return "unfencing";
2144 } else if (strcmp(action, PCMK_ACTION_OFF) == 0) {
2145 return "turning off";
2146 } else {
2147 return action;
2148 }
2149 }
2150
2151 /*!
2152 * \internal
2153 * \brief Parse a target name from one line of a target list string
2154 *
2155 * \param[in] line One line of a target list string
2156 * \param[in] len String length of line
2157 * \param[in,out] output List to add newly allocated target name to
2158 */
2159 static void
2160 parse_list_line(const char *line, int len, GList **output)
2161 {
2162 size_t i = 0;
2163 size_t entry_start = 0;
2164
2165 /* Skip complaints about additional parameters device doesn't understand
2166 *
2167 * @TODO Document or eliminate the implied restriction of target names
2168 */
2169 if (strstr(line, "invalid") || strstr(line, "variable")) {
2170 crm_debug("Skipping list output line: %s", line);
2171 return;
2172 }
2173
2174 // Process line content, character by character
2175 for (i = 0; i <= len; i++) {
2176
2177 if (isspace(line[i]) || (line[i] == ',') || (line[i] == ';')
2178 || (line[i] == '\0')) {
2179 // We've found a separator (i.e. the end of an entry)
2180
2181 int rc = 0;
2182 char *entry = NULL;
2183
2184 if (i == entry_start) {
2185 // Skip leading and sequential separators
2186 entry_start = i + 1;
2187 continue;
2188 }
2189
2190 entry = pcmk__assert_alloc(i - entry_start + 1, sizeof(char));
2191
2192 /* Read entry, stopping at first separator
2193 *
2194 * @TODO Document or eliminate these character restrictions
2195 */
2196 rc = sscanf(line + entry_start, "%[a-zA-Z0-9_-.]", entry);
2197 if (rc != 1) {
2198 crm_warn("Could not parse list output entry: %s "
2199 QB_XS " entry_start=%d position=%d",
2200 line + entry_start, entry_start, i);
2201 free(entry);
2202
2203 } else if (pcmk__strcase_any_of(entry, PCMK_ACTION_ON,
2204 PCMK_ACTION_OFF, NULL)) {
2205 /* Some agents print the target status in the list output,
2206 * though none are known now (the separate list-status command
2207 * is used for this, but it can also print "UNKNOWN"). To handle
2208 * this possibility, skip such entries.
2209 *
2210 * @TODO Document or eliminate the implied restriction of target
2211 * names.
2212 */
2213 free(entry);
2214
2215 } else {
2216 // We have a valid entry
2217 *output = g_list_append(*output, entry);
2218 }
2219 entry_start = i + 1;
2220 }
2221 }
2222 }
2223
2224 /*!
2225 * \internal
2226 * \brief Parse a list of targets from a string
2227 *
2228 * \param[in] list_output Target list as a string
2229 *
2230 * \return List of target names
2231 * \note The target list string format is flexible, to allow for user-specified
2232 * lists such pcmk_host_list and the output of an agent's list action
2233 * (whether direct or via the API, which escapes newlines). There may be
2234 * multiple lines, separated by either a newline or an escaped newline
2235 * (backslash n). Each line may have one or more target names, separated
2236 * by any combination of whitespace, commas, and semi-colons. Lines
2237 * containing "invalid" or "variable" will be ignored entirely. Target
2238 * names "on" or "off" (case-insensitive) will be ignored. Target names
2239 * may contain only alphanumeric characters, underbars (_), dashes (-),
2240 * and dots (.) (if any other character occurs in the name, it and all
2241 * subsequent characters in the name will be ignored).
2242 * \note The caller is responsible for freeing the result with
2243 * g_list_free_full(result, free).
2244 */
2245 GList *
2246 stonith__parse_targets(const char *target_spec)
2247 {
2248 GList *targets = NULL;
2249
2250 if (target_spec != NULL) {
2251 size_t out_len = strlen(target_spec);
2252 size_t line_start = 0; // Starting index of line being processed
2253
2254 for (size_t i = 0; i <= out_len; ++i) {
2255 if ((target_spec[i] == '\n') || (target_spec[i] == '\0')
2256 || ((target_spec[i] == '\\') && (target_spec[i + 1] == 'n'))) {
2257 // We've reached the end of one line of output
2258
2259 int len = i - line_start;
2260
2261 if (len > 0) {
2262 char *line = strndup(target_spec + line_start, len);
2263
2264 line[len] = '\0'; // Because it might be a newline
2265 parse_list_line(line, len, &targets);
2266 free(line);
2267 }
2268 if (target_spec[i] == '\\') {
2269 ++i; // backslash-n takes up two positions
2270 }
2271 line_start = i + 1;
2272 }
2273 }
2274 }
2275 return targets;
2276 }
2277
2278 /*!
2279 * \internal
2280 * \brief Check whether a fencing failure was followed by an equivalent success
2281 *
2282 * \param[in] event Fencing failure
2283 * \param[in] top_history Complete fencing history (must be sorted by
2284 * stonith__sort_history() beforehand)
2285 *
2286 * \return The name of the node that executed the fencing if a later successful
2287 * event exists, or NULL if no such event exists
2288 */
2289 const char *
2290 stonith__later_succeeded(const stonith_history_t *event,
2291 const stonith_history_t *top_history)
2292 {
2293 const char *other = NULL;
2294
2295 for (const stonith_history_t *prev_hp = top_history;
2296 prev_hp != NULL; prev_hp = prev_hp->next) {
2297 if (prev_hp == event) {
2298 break;
2299 }
2300 if ((prev_hp->state == st_done) &&
2301 pcmk__str_eq(event->target, prev_hp->target, pcmk__str_casei) &&
2302 pcmk__str_eq(event->action, prev_hp->action, pcmk__str_none) &&
2303 ((event->completed < prev_hp->completed) ||
2304 ((event->completed == prev_hp->completed) && (event->completed_nsec < prev_hp->completed_nsec)))) {
2305
2306 if ((event->delegate == NULL)
2307 || pcmk__str_eq(event->delegate, prev_hp->delegate,
2308 pcmk__str_casei)) {
2309 // Prefer equivalent fencing by same executioner
2310 return prev_hp->delegate;
2311
2312 } else if (other == NULL) {
2313 // Otherwise remember first successful executioner
2314 other = (prev_hp->delegate == NULL)? "some node" : prev_hp->delegate;
2315 }
2316 }
2317 }
2318 return other;
2319 }
2320
2321 /*!
2322 * \internal
2323 * \brief Sort fencing history, pending first then by most recently completed
2324 *
2325 * \param[in,out] history List of stonith actions
2326 *
2327 * \return New head of sorted \p history
2328 */
2329 stonith_history_t *
2330 stonith__sort_history(stonith_history_t *history)
2331 {
2332 stonith_history_t *new = NULL, *pending = NULL, *hp, *np, *tmp;
2333
2334 for (hp = history; hp; ) {
2335 tmp = hp->next;
2336 if ((hp->state == st_done) || (hp->state == st_failed)) {
2337 /* sort into new */
2338 if ((!new) || (hp->completed > new->completed) ||
2339 ((hp->completed == new->completed) && (hp->completed_nsec > new->completed_nsec))) {
2340 hp->next = new;
2341 new = hp;
2342 } else {
2343 np = new;
2344 do {
2345 if ((!np->next) || (hp->completed > np->next->completed) ||
2346 ((hp->completed == np->next->completed) && (hp->completed_nsec > np->next->completed_nsec))) {
2347 hp->next = np->next;
2348 np->next = hp;
2349 break;
2350 }
2351 np = np->next;
2352 } while (1);
2353 }
2354 } else {
2355 /* put into pending */
2356 hp->next = pending;
2357 pending = hp;
2358 }
2359 hp = tmp;
2360 }
2361
2362 /* pending actions don't have a completed-stamp so make them go front */
2363 if (pending) {
2364 stonith_history_t *last_pending = pending;
2365
2366 while (last_pending->next) {
2367 last_pending = last_pending->next;
2368 }
2369
2370 last_pending->next = new;
2371 new = pending;
2372 }
2373 return new;
2374 }
2375
2376 /*!
2377 * \brief Return string equivalent of an operation state value
2378 *
2379 * \param[in] state Fencing operation state value
2380 *
2381 * \return Human-friendly string equivalent of state
2382 */
2383 const char *
2384 stonith_op_state_str(enum op_state state)
2385 {
2386 switch (state) {
2387 case st_query: return "querying";
2388 case st_exec: return "executing";
2389 case st_done: return "completed";
2390 case st_duplicate: return "duplicate";
2391 case st_failed: return "failed";
2392 }
2393 return "unknown";
2394 }
2395
2396 stonith_history_t *
2397 stonith__first_matching_event(stonith_history_t *history,
2398 bool (*matching_fn)(stonith_history_t *, void *),
2399 void *user_data)
2400 {
2401 for (stonith_history_t *hp = history; hp; hp = hp->next) {
2402 if (matching_fn(hp, user_data)) {
2403 return hp;
2404 }
2405 }
2406
2407 return NULL;
2408 }
2409
2410 bool
2411 stonith__event_state_pending(stonith_history_t *history, void *user_data)
2412 {
2413 return history->state != st_failed && history->state != st_done;
2414 }
2415
2416 bool
2417 stonith__event_state_eq(stonith_history_t *history, void *user_data)
2418 {
2419 return history->state == GPOINTER_TO_INT(user_data);
2420 }
2421
2422 bool
2423 stonith__event_state_neq(stonith_history_t *history, void *user_data)
2424 {
2425 return history->state != GPOINTER_TO_INT(user_data);
2426 }
2427
2428 void
2429 stonith__device_parameter_flags(uint32_t *device_flags, const char *device_name,
2430 xmlNode *metadata)
2431 {
2432 xmlXPathObjectPtr xpath = NULL;
2433 int max = 0;
2434 int lpc = 0;
2435
2436 CRM_CHECK((device_flags != NULL) && (metadata != NULL), return);
2437
2438 xpath = xpath_search(metadata, "//" PCMK_XE_PARAMETER);
2439 max = numXpathResults(xpath);
2440
2441 if (max <= 0) {
2442 freeXpathObject(xpath);
2443 return;
2444 }
2445
2446 for (lpc = 0; lpc < max; lpc++) {
2447 const char *parameter = NULL;
2448 xmlNode *match = getXpathResult(xpath, lpc);
2449
2450 CRM_LOG_ASSERT(match != NULL);
2451 if (match == NULL) {
2452 continue;
2453 }
2454
2455 parameter = crm_element_value(match, PCMK_XA_NAME);
2456
2457 if (pcmk__str_eq(parameter, "plug", pcmk__str_casei)) {
2458 stonith__set_device_flags(*device_flags, device_name,
2459 st_device_supports_parameter_plug);
2460
2461 } else if (pcmk__str_eq(parameter, "port", pcmk__str_casei)) {
2462 stonith__set_device_flags(*device_flags, device_name,
2463 st_device_supports_parameter_port);
2464 }
2465 }
2466
2467 freeXpathObject(xpath);
2468 }
2469
2470 /*!
2471 * \internal
2472 * \brief Retrieve fence agent meta-data asynchronously
2473 *
2474 * \param[in] agent Agent to execute
2475 * \param[in] timeout_sec Error if not complete within this time
2476 * \param[in] callback Function to call with result (this will always be
2477 * called, whether by this function directly or
2478 * later via the main loop, and on success the
2479 * metadata will be in its result argument's
2480 * action_stdout)
2481 * \param[in,out] user_data User data to pass to callback
2482 *
2483 * \return Standard Pacemaker return code
2484 * \note The caller must use a main loop. This function is not a
2485 * stonith_api_operations_t method because it does not need a stonith_t
2486 * object and does not go through the fencer, but executes the agent
2487 * directly.
2488 */
2489 int
2490 stonith__metadata_async(const char *agent, int timeout_sec,
2491 void (*callback)(int pid,
2492 const pcmk__action_result_t *result,
2493 void *user_data),
2494 void *user_data)
2495 {
2496 switch (stonith_get_namespace(agent, NULL)) {
2497 case st_namespace_rhcs:
2498 {
2499 stonith_action_t *action = NULL;
2500 int rc = pcmk_ok;
2501
2502 action = stonith__action_create(agent, PCMK_ACTION_METADATA,
2503 NULL, 0, timeout_sec, NULL,
2504 NULL, NULL);
2505
2506 rc = stonith__execute_async(action, user_data, callback, NULL);
2507 if (rc != pcmk_ok) {
2508 callback(0, stonith__action_result(action), user_data);
2509 stonith__destroy_action(action);
2510 }
2511 return pcmk_legacy2rc(rc);
2512 }
2513
2514 #if HAVE_STONITH_STONITH_H
2515 case st_namespace_lha:
2516 // LHA metadata is simply synthesized, so simulate async
2517 {
2518 pcmk__action_result_t result = {
2519 .exit_status = CRM_EX_OK,
2520 .execution_status = PCMK_EXEC_DONE,
2521 .exit_reason = NULL,
2522 .action_stdout = NULL,
2523 .action_stderr = NULL,
2524 };
2525
2526 stonith__lha_metadata(agent, timeout_sec,
2527 &result.action_stdout);
2528 callback(0, &result, user_data);
2529 pcmk__reset_result(&result);
2530 return pcmk_rc_ok;
2531 }
2532 #endif
2533
2534 default:
2535 {
2536 pcmk__action_result_t result = {
2537 .exit_status = CRM_EX_NOSUCH,
2538 .execution_status = PCMK_EXEC_ERROR_HARD,
2539 .exit_reason = crm_strdup_printf("No such agent '%s'",
2540 agent),
2541 .action_stdout = NULL,
2542 .action_stderr = NULL,
2543 };
2544
2545 callback(0, &result, user_data);
2546 pcmk__reset_result(&result);
2547 return ENOENT;
2548 }
2549 }
2550 }
2551
2552 /*!
2553 * \internal
2554 * \brief Return the exit status from an async action callback
2555 *
2556 * \param[in] data Callback data
2557 *
2558 * \return Exit status from callback data
2559 */
2560 int
2561 stonith__exit_status(const stonith_callback_data_t *data)
2562 {
2563 if ((data == NULL) || (data->opaque == NULL)) {
2564 return CRM_EX_ERROR;
2565 }
2566 return ((pcmk__action_result_t *) data->opaque)->exit_status;
2567 }
2568
2569 /*!
2570 * \internal
2571 * \brief Return the execution status from an async action callback
2572 *
2573 * \param[in] data Callback data
2574 *
2575 * \return Execution status from callback data
2576 */
2577 int
2578 stonith__execution_status(const stonith_callback_data_t *data)
2579 {
2580 if ((data == NULL) || (data->opaque == NULL)) {
2581 return PCMK_EXEC_UNKNOWN;
2582 }
2583 return ((pcmk__action_result_t *) data->opaque)->execution_status;
2584 }
2585
2586 /*!
2587 * \internal
2588 * \brief Return the exit reason from an async action callback
2589 *
2590 * \param[in] data Callback data
2591 *
2592 * \return Exit reason from callback data
2593 */
2594 const char *
2595 stonith__exit_reason(const stonith_callback_data_t *data)
2596 {
2597 if ((data == NULL) || (data->opaque == NULL)) {
2598 return NULL;
2599 }
2600 return ((pcmk__action_result_t *) data->opaque)->exit_reason;
2601 }
2602
2603 /*!
2604 * \internal
2605 * \brief Return the exit status from an event notification
2606 *
2607 * \param[in] event Event
2608 *
2609 * \return Exit status from event
2610 */
2611 int
2612 stonith__event_exit_status(const stonith_event_t *event)
2613 {
2614 if ((event == NULL) || (event->opaque == NULL)) {
2615 return CRM_EX_ERROR;
2616 } else {
2617 struct event_private *event_private = event->opaque;
2618
2619 return event_private->result.exit_status;
2620 }
2621 }
2622
2623 /*!
2624 * \internal
2625 * \brief Return the execution status from an event notification
2626 *
2627 * \param[in] event Event
2628 *
2629 * \return Execution status from event
2630 */
2631 int
2632 stonith__event_execution_status(const stonith_event_t *event)
2633 {
2634 if ((event == NULL) || (event->opaque == NULL)) {
2635 return PCMK_EXEC_UNKNOWN;
2636 } else {
2637 struct event_private *event_private = event->opaque;
2638
2639 return event_private->result.execution_status;
2640 }
2641 }
2642
2643 /*!
2644 * \internal
2645 * \brief Return the exit reason from an event notification
2646 *
2647 * \param[in] event Event
2648 *
2649 * \return Exit reason from event
2650 */
2651 const char *
2652 stonith__event_exit_reason(const stonith_event_t *event)
2653 {
2654 if ((event == NULL) || (event->opaque == NULL)) {
2655 return NULL;
2656 } else {
2657 struct event_private *event_private = event->opaque;
2658
2659 return event_private->result.exit_reason;
2660 }
2661 }
2662
2663 /*!
2664 * \internal
2665 * \brief Return a human-friendly description of a fencing event
2666 *
2667 * \param[in] event Event to describe
2668 *
2669 * \return Newly allocated string with description of \p event
2670 * \note The caller is responsible for freeing the return value.
2671 * This function asserts on memory errors and never returns NULL.
2672 */
2673 char *
2674 stonith__event_description(const stonith_event_t *event)
2675 {
2676 // Use somewhat readable defaults
2677 const char *origin = pcmk__s(event->client_origin, "a client");
2678 const char *origin_node = pcmk__s(event->origin, "a node");
2679 const char *executioner = pcmk__s(event->executioner, "the cluster");
2680 const char *device = pcmk__s(event->device, "unknown");
2681 const char *action = pcmk__s(event->action, event->operation);
2682 const char *target = pcmk__s(event->target, "no node");
2683 const char *reason = stonith__event_exit_reason(event);
2684 const char *status;
2685
2686 if (action == NULL) {
2687 action = "(unknown)";
2688 }
2689
2690 if (stonith__event_execution_status(event) != PCMK_EXEC_DONE) {
2691 status = pcmk_exec_status_str(stonith__event_execution_status(event));
2692 } else if (stonith__event_exit_status(event) != CRM_EX_OK) {
2693 status = pcmk_exec_status_str(PCMK_EXEC_ERROR);
2694 } else {
2695 status = crm_exit_str(CRM_EX_OK);
2696 }
2697
2698 if (pcmk__str_eq(event->operation, PCMK__VALUE_ST_NOTIFY_HISTORY,
2699 pcmk__str_none)) {
2700 return crm_strdup_printf("Fencing history may have changed");
2701
2702 } else if (pcmk__str_eq(event->operation, STONITH_OP_DEVICE_ADD,
2703 pcmk__str_none)) {
2704 return crm_strdup_printf("A fencing device (%s) was added", device);
2705
2706 } else if (pcmk__str_eq(event->operation, STONITH_OP_DEVICE_DEL,
2707 pcmk__str_none)) {
2708 return crm_strdup_printf("A fencing device (%s) was removed", device);
2709
2710 } else if (pcmk__str_eq(event->operation, STONITH_OP_LEVEL_ADD,
2711 pcmk__str_none)) {
2712 return crm_strdup_printf("A fencing topology level (%s) was added",
2713 device);
2714
2715 } else if (pcmk__str_eq(event->operation, STONITH_OP_LEVEL_DEL,
2716 pcmk__str_none)) {
2717 return crm_strdup_printf("A fencing topology level (%s) was removed",
2718 device);
2719 }
2720
2721 // event->operation should be PCMK__VALUE_ST_NOTIFY_FENCE at this point
2722
2723 return crm_strdup_printf("Operation %s of %s by %s for %s@%s: %s%s%s%s (ref=%s)",
2724 action, target, executioner, origin, origin_node,
2725 status,
2726 ((reason == NULL)? "" : " ("), pcmk__s(reason, ""),
2727 ((reason == NULL)? "" : ")"),
2728 pcmk__s(event->id, "(none)"));
2729 }
2730