pacemaker  2.1.4-dc6eb4362
Scalable High-Availability cluster resource manager
st_client.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2022 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/msg_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 
45 
46 // Used as stonith_event_t:opaque
47 struct event_private {
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 
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 
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;
80 };
81 
82 typedef int (*stonith_op_t) (const char *, int, const char *, xmlNode *,
83  xmlNode *, xmlNode *, xmlNode **, xmlNode **);
84 
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);
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 
129 const char *
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 
151 stonith_get_namespace(const char *agent, const char *namespace_s)
152 {
153  if (pcmk__str_eq(namespace_s, "internal", pcmk__str_casei)) {
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  crm_err("Unknown fence agent: %s", agent);
168  return st_namespace_invalid;
169 }
170 
171 gboolean
173 {
174  gboolean rv = FALSE;
175  stonith_t *stonith_api = st?st:stonith_api_new();
176  char *list = NULL;
177 
178  if(stonith_api) {
179  if (stonith_api->state == stonith_disconnected) {
180  int rc = stonith_api->cmds->connect(stonith_api, "stonith-api", NULL);
181 
182  if (rc != pcmk_ok) {
183  crm_err("Failed connecting to Stonith-API for watchdog-fencing-query.");
184  }
185  }
186 
187  if (stonith_api->state != stonith_disconnected) {
188  /* caveat!!!
189  * this might fail when when stonithd is just updating the device-list
190  * probably something we should fix as well for other api-calls */
191  int rc = stonith_api->cmds->list(stonith_api, st_opt_sync_call, STONITH_WATCHDOG_ID, &list, 0);
192  if ((rc != pcmk_ok) || (list == NULL)) {
193  /* due to the race described above it can happen that
194  * we drop in here - so as not to make remote nodes
195  * panic on that answer
196  */
197  if (rc == -ENODEV) {
198  crm_notice("Cluster does not have watchdog fencing device");
199  } else {
200  crm_warn("Could not check for watchdog fencing device: %s",
201  pcmk_strerror(rc));
202  }
203  } else if (list[0] == '\0') {
204  rv = TRUE;
205  } else {
206  GList *targets = stonith__parse_targets(list);
207  rv = pcmk__str_in_list(node, targets, pcmk__str_casei);
208  g_list_free_full(targets, free);
209  }
210  free(list);
211  if (!st) {
212  /* if we're provided the api we still might have done the
213  * connection - but let's assume the caller won't bother
214  */
215  stonith_api->cmds->disconnect(stonith_api);
216  }
217  }
218 
219  if (!st) {
220  stonith_api_delete(stonith_api);
221  }
222  } else {
223  crm_err("Stonith-API for watchdog-fencing-query couldn't be created.");
224  }
225  crm_trace("Pacemaker assumes node %s %sto do watchdog-fencing.",
226  node, rv?"":"not ");
227  return rv;
228 }
229 
230 gboolean
232 {
234 }
235 
236 /* when cycling through the list we don't want to delete items
237  so just mark them and when we know nobody is using the list
238  loop over it to remove the marked items
239  */
240 static void
241 foreach_notify_entry (stonith_private_t *private,
242  GFunc func,
243  gpointer user_data)
244 {
245  private->notify_refcnt++;
246  g_list_foreach(private->notify_list, func, user_data);
247  private->notify_refcnt--;
248  if ((private->notify_refcnt == 0) &&
249  private->notify_deletes) {
250  GList *list_item = private->notify_list;
251 
252  private->notify_deletes = FALSE;
253  while (list_item != NULL)
254  {
255  stonith_notify_client_t *list_client = list_item->data;
256  GList *next = g_list_next(list_item);
257 
258  if (list_client->delete) {
259  free(list_client);
260  private->notify_list =
261  g_list_delete_link(private->notify_list, list_item);
262  }
263  list_item = next;
264  }
265  }
266 }
267 
268 static void
269 stonith_connection_destroy(gpointer user_data)
270 {
271  stonith_t *stonith = user_data;
272  stonith_private_t *native = NULL;
273  struct notify_blob_s blob;
274 
275  crm_trace("Sending destroyed notification");
276  blob.stonith = stonith;
277  blob.xml = create_xml_node(NULL, "notify");
278 
279  native = stonith->st_private;
280  native->ipc = NULL;
281  native->source = NULL;
282 
283  free(native->token); native->token = NULL;
284  stonith->state = stonith_disconnected;
285  crm_xml_add(blob.xml, F_TYPE, T_STONITH_NOTIFY);
287 
288  foreach_notify_entry(native, stonith_send_notification, &blob);
289  free_xml(blob.xml);
290 }
291 
292 xmlNode *
293 create_device_registration_xml(const char *id, enum stonith_namespace namespace,
294  const char *agent, stonith_key_value_t *params,
295  const char *rsc_provides)
296 {
297  xmlNode *data = create_xml_node(NULL, F_STONITH_DEVICE);
298  xmlNode *args = create_xml_node(data, XML_TAG_ATTRS);
299 
300 #if HAVE_STONITH_STONITH_H
301  if (namespace == st_namespace_any) {
302  namespace = stonith_get_namespace(agent, NULL);
303  }
304  if (namespace == st_namespace_lha) {
305  hash2field((gpointer) "plugin", (gpointer) agent, args);
306  agent = "fence_legacy";
307  }
308 #endif
309 
311  crm_xml_add(data, F_STONITH_ORIGIN, __func__);
312  crm_xml_add(data, "agent", agent);
313  if ((namespace != st_namespace_any) && (namespace != st_namespace_invalid)) {
314  crm_xml_add(data, "namespace", stonith_namespace2text(namespace));
315  }
316  if (rsc_provides) {
317  crm_xml_add(data, "rsc_provides", rsc_provides);
318  }
319 
320  for (; params; params = params->next) {
321  hash2field((gpointer) params->key, (gpointer) params->value, args);
322  }
323 
324  return data;
325 }
326 
327 static int
328 stonith_api_register_device(stonith_t * st, int call_options,
329  const char *id, const char *namespace, const char *agent,
330  stonith_key_value_t * params)
331 {
332  int rc = 0;
333  xmlNode *data = NULL;
334 
336  agent, params, NULL);
337 
338  rc = stonith_send_command(st, STONITH_OP_DEVICE_ADD, data, NULL, call_options, 0);
339  free_xml(data);
340 
341  return rc;
342 }
343 
344 static int
345 stonith_api_remove_device(stonith_t * st, int call_options, const char *name)
346 {
347  int rc = 0;
348  xmlNode *data = NULL;
349 
351  crm_xml_add(data, F_STONITH_ORIGIN, __func__);
353  rc = stonith_send_command(st, STONITH_OP_DEVICE_DEL, data, NULL, call_options, 0);
354  free_xml(data);
355 
356  return rc;
357 }
358 
359 static int
360 stonith_api_remove_level_full(stonith_t *st, int options,
361  const char *node, const char *pattern,
362  const char *attr, const char *value, int level)
363 {
364  int rc = 0;
365  xmlNode *data = NULL;
366 
367  CRM_CHECK(node || pattern || (attr && value), return -EINVAL);
368 
370  crm_xml_add(data, F_STONITH_ORIGIN, __func__);
371 
372  if (node) {
374 
375  } else if (pattern) {
377 
378  } else {
381  }
382 
384  rc = stonith_send_command(st, STONITH_OP_LEVEL_DEL, data, NULL, options, 0);
385  free_xml(data);
386 
387  return rc;
388 }
389 
390 static int
391 stonith_api_remove_level(stonith_t * st, int options, const char *node, int level)
392 {
393  return stonith_api_remove_level_full(st, options, node,
394  NULL, NULL, NULL, level);
395 }
396 
412 xmlNode *
413 create_level_registration_xml(const char *node, const char *pattern,
414  const char *attr, const char *value,
415  int level, stonith_key_value_t *device_list)
416 {
417  size_t len = 0;
418  char *list = NULL;
419  xmlNode *data;
420 
421  CRM_CHECK(node || pattern || (attr && value), return NULL);
422 
424  CRM_CHECK(data, return NULL);
425 
426  crm_xml_add(data, F_STONITH_ORIGIN, __func__);
429 
430  if (node) {
432 
433  } else if (pattern) {
435 
436  } else {
439  }
440 
441  // cppcheck seems not to understand the abort logic behind pcmk__realloc
442  // cppcheck-suppress memleak
443  for (; device_list; device_list = device_list->next) {
444  pcmk__add_separated_word(&list, &len, device_list->value, ",");
445  }
446 
448 
449  free(list);
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,
456  const char *attr, const char *value,
457  int level, 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  free_xml(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  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, const char *namespace,
480  stonith_key_value_t ** devices, int timeout)
481 {
482  int count = 0;
483  enum stonith_namespace ns = stonith_text2namespace(namespace);
484 
485  if (devices == NULL) {
486  crm_err("Parameter error: stonith_api_device_list");
487  return -EFAULT;
488  }
489 
490 #if HAVE_STONITH_STONITH_H
491  // Include Linux-HA agents if requested
492  if ((ns == st_namespace_any) || (ns == st_namespace_lha)) {
493  count += stonith__list_lha_agents(devices);
494  }
495 #endif
496 
497  // Include Red Hat agents if requested
498  if ((ns == st_namespace_any) || (ns == st_namespace_rhcs)) {
499  count += stonith__list_rhcs_agents(devices);
500  }
501 
502  return count;
503 }
504 
505 static int
506 stonith_api_device_metadata(stonith_t * stonith, int call_options, const char *agent,
507  const char *namespace, char **output, int timeout)
508 {
509  /* By executing meta-data directly, we can get it from stonith_admin when
510  * the cluster is not running, which is important for higher-level tools.
511  */
512 
513  enum stonith_namespace ns = stonith_get_namespace(agent, namespace);
514 
515  crm_trace("Looking up metadata for %s agent %s",
516  stonith_namespace2text(ns), agent);
517 
518  switch (ns) {
519  case st_namespace_rhcs:
520  return stonith__rhcs_metadata(agent, timeout, output);
521 
522 #if HAVE_STONITH_STONITH_H
523  case st_namespace_lha:
524  return stonith__lha_metadata(agent, timeout, output);
525 #endif
526 
527  default:
528  crm_err("Can't get fence agent '%s' meta-data: No such agent",
529  agent);
530  break;
531  }
532  return -ENODEV;
533 }
534 
535 static int
536 stonith_api_query(stonith_t * stonith, int call_options, const char *target,
537  stonith_key_value_t ** devices, int timeout)
538 {
539  int rc = 0, lpc = 0, max = 0;
540 
541  xmlNode *data = NULL;
542  xmlNode *output = NULL;
543  xmlXPathObjectPtr xpathObj = NULL;
544 
545  CRM_CHECK(devices != NULL, return -EINVAL);
546 
548  crm_xml_add(data, F_STONITH_ORIGIN, __func__);
551  rc = stonith_send_command(stonith, STONITH_OP_QUERY, data, &output, call_options, timeout);
552 
553  if (rc < 0) {
554  return rc;
555  }
556 
557  xpathObj = xpath_search(output, "//@agent");
558  if (xpathObj) {
559  max = numXpathResults(xpathObj);
560 
561  for (lpc = 0; lpc < max; lpc++) {
562  xmlNode *match = getXpathResult(xpathObj, lpc);
563 
564  CRM_LOG_ASSERT(match != NULL);
565  if(match != NULL) {
566  xmlChar *match_path = xmlGetNodePath(match);
567 
568  crm_info("%s[%d] = %s", "//@agent", lpc, match_path);
569  free(match_path);
570  *devices = stonith_key_value_add(*devices, NULL, crm_element_value(match, XML_ATTR_ID));
571  }
572  }
573 
574  freeXpathObject(xpathObj);
575  }
576 
577  free_xml(output);
578  free_xml(data);
579  return max;
580 }
581 
582 static int
583 stonith_api_call(stonith_t * stonith,
584  int call_options,
585  const char *id,
586  const char *action, const char *victim, int timeout, xmlNode ** output)
587 {
588  int rc = 0;
589  xmlNode *data = NULL;
590 
592  crm_xml_add(data, F_STONITH_ORIGIN, __func__);
596 
597  rc = stonith_send_command(stonith, STONITH_OP_EXEC, data, output, call_options, timeout);
598  free_xml(data);
599 
600  return rc;
601 }
602 
603 static int
604 stonith_api_list(stonith_t * stonith, int call_options, const char *id, char **list_info,
605  int timeout)
606 {
607  int rc;
608  xmlNode *output = NULL;
609 
610  rc = stonith_api_call(stonith, call_options, id, "list", NULL, timeout, &output);
611 
612  if (output && list_info) {
613  const char *list_str;
614 
615  list_str = crm_element_value(output, F_STONITH_OUTPUT);
616 
617  if (list_str) {
618  *list_info = strdup(list_str);
619  }
620  }
621 
622  if (output) {
623  free_xml(output);
624  }
625 
626  return rc;
627 }
628 
629 static int
630 stonith_api_monitor(stonith_t * stonith, int call_options, const char *id, int timeout)
631 {
632  return stonith_api_call(stonith, call_options, id, "monitor", NULL, timeout, NULL);
633 }
634 
635 static int
636 stonith_api_status(stonith_t * stonith, int call_options, const char *id, const char *port,
637  int timeout)
638 {
639  return stonith_api_call(stonith, call_options, id, "status", port, timeout, NULL);
640 }
641 
642 static int
643 stonith_api_fence_with_delay(stonith_t * stonith, int call_options, const char *node,
644  const char *action, int timeout, int tolerance, int delay)
645 {
646  int rc = 0;
647  xmlNode *data = NULL;
648 
649  data = create_xml_node(NULL, __func__);
655 
656  rc = stonith_send_command(stonith, STONITH_OP_FENCE, data, NULL, call_options, timeout);
657  free_xml(data);
658 
659  return rc;
660 }
661 
662 static int
663 stonith_api_fence(stonith_t * stonith, int call_options, const char *node, const char *action,
664  int timeout, int tolerance)
665 {
666  return stonith_api_fence_with_delay(stonith, call_options, node, action,
667  timeout, tolerance, 0);
668 }
669 
670 static int
671 stonith_api_confirm(stonith_t * stonith, int call_options, const char *target)
672 {
674  return stonith_api_fence(stonith, call_options, target, "off", 0, 0);
675 }
676 
677 static int
678 stonith_api_history(stonith_t * stonith, int call_options, const char *node,
679  stonith_history_t ** history, int timeout)
680 {
681  int rc = 0;
682  xmlNode *data = NULL;
683  xmlNode *output = NULL;
684  stonith_history_t *last = NULL;
685 
686  *history = NULL;
687 
688  if (node) {
689  data = create_xml_node(NULL, __func__);
691  }
692 
693  stonith__set_call_options(call_options, node, st_opt_sync_call);
694  rc = stonith_send_command(stonith, STONITH_OP_FENCE_HISTORY, data, &output,
695  call_options, timeout);
696  free_xml(data);
697 
698  if (rc == 0) {
699  xmlNode *op = NULL;
700  xmlNode *reply = get_xpath_object("//" F_STONITH_HISTORY_LIST, output,
701  LOG_NEVER);
702 
703  for (op = pcmk__xml_first_child(reply); op != NULL;
704  op = pcmk__xml_next(op)) {
705  stonith_history_t *kvp;
706  long long completed;
707  long long completed_nsec = 0L;
708 
709  kvp = calloc(1, sizeof(stonith_history_t));
715  crm_element_value_ll(op, F_STONITH_DATE, &completed);
716  kvp->completed = (time_t) completed;
717  crm_element_value_ll(op, F_STONITH_DATE_NSEC, &completed_nsec);
718  kvp->completed_nsec = completed_nsec;
722 
723  if (last) {
724  last->next = kvp;
725  } else {
726  *history = kvp;
727  }
728  last = kvp;
729  }
730  }
731 
732  free_xml(output);
733 
734  return rc;
735 }
736 
738 {
739  stonith_history_t *hp, *hp_old;
740 
741  for (hp = history; hp; hp_old = hp, hp = hp->next, free(hp_old)) {
742  free(hp->target);
743  free(hp->action);
744  free(hp->origin);
745  free(hp->delegate);
746  free(hp->client);
747  free(hp->exit_reason);
748  }
749 }
750 
751 static gint
752 stonithlib_GCompareFunc(gconstpointer a, gconstpointer b)
753 {
754  int rc = 0;
755  const stonith_notify_client_t *a_client = a;
756  const stonith_notify_client_t *b_client = b;
757 
758  if (a_client->delete || b_client->delete) {
759  /* make entries marked for deletion not findable */
760  return -1;
761  }
762  CRM_CHECK(a_client->event != NULL && b_client->event != NULL, return 0);
763  rc = strcmp(a_client->event, b_client->event);
764  if (rc == 0) {
765  if (a_client->notify == NULL || b_client->notify == NULL) {
766  return 0;
767 
768  } else if (a_client->notify == b_client->notify) {
769  return 0;
770 
771  } else if (((long)a_client->notify) < ((long)b_client->notify)) {
772  crm_err("callbacks for %s are not equal: %p vs. %p",
773  a_client->event, a_client->notify, b_client->notify);
774  return -1;
775  }
776  crm_err("callbacks for %s are not equal: %p vs. %p",
777  a_client->event, a_client->notify, b_client->notify);
778  return 1;
779  }
780  return rc;
781 }
782 
783 xmlNode *
784 stonith_create_op(int call_id, const char *token, const char *op, xmlNode * data, int call_options)
785 {
786  xmlNode *op_msg = create_xml_node(NULL, "stonith_command");
787 
788  CRM_CHECK(op_msg != NULL, return NULL);
789  CRM_CHECK(token != NULL, return NULL);
790 
791  crm_xml_add(op_msg, F_XML_TAGNAME, "stonith_command");
792 
793  crm_xml_add(op_msg, F_TYPE, T_STONITH_NG);
794  crm_xml_add(op_msg, F_STONITH_CALLBACK_TOKEN, token);
795  crm_xml_add(op_msg, F_STONITH_OPERATION, op);
796  crm_xml_add_int(op_msg, F_STONITH_CALLID, call_id);
797  crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options);
798  crm_xml_add_int(op_msg, F_STONITH_CALLOPTS, call_options);
799 
800  if (data != NULL) {
802  }
803 
804  return op_msg;
805 }
806 
807 static void
808 stonith_destroy_op_callback(gpointer data)
809 {
811 
812  if (blob->timer && blob->timer->ref > 0) {
813  g_source_remove(blob->timer->ref);
814  }
815  free(blob->timer);
816  free(blob);
817 }
818 
819 static int
820 stonith_api_signoff(stonith_t * stonith)
821 {
822  stonith_private_t *native = stonith->st_private;
823 
824  crm_debug("Disconnecting from the fencer");
825 
826  if (native->source != NULL) {
827  /* Attached to mainloop */
828  mainloop_del_ipc_client(native->source);
829  native->source = NULL;
830  native->ipc = NULL;
831 
832  } else if (native->ipc) {
833  /* Not attached to mainloop */
834  crm_ipc_t *ipc = native->ipc;
835 
836  native->ipc = NULL;
837  crm_ipc_close(ipc);
838  crm_ipc_destroy(ipc);
839  }
840 
841  free(native->token); native->token = NULL;
842  stonith->state = stonith_disconnected;
843  return pcmk_ok;
844 }
845 
846 static int
847 stonith_api_del_callback(stonith_t * stonith, int call_id, bool all_callbacks)
848 {
849  stonith_private_t *private = stonith->st_private;
850 
851  if (all_callbacks) {
852  private->op_callback = NULL;
853  g_hash_table_destroy(private->stonith_op_callback_table);
854  private->stonith_op_callback_table = pcmk__intkey_table(stonith_destroy_op_callback);
855 
856  } else if (call_id == 0) {
857  private->op_callback = NULL;
858 
859  } else {
860  pcmk__intkey_table_remove(private->stonith_op_callback_table, call_id);
861  }
862  return pcmk_ok;
863 }
864 
876 static void
877 invoke_fence_action_callback(stonith_t *st, int call_id,
879  void *userdata,
880  void (*callback) (stonith_t *st,
882 {
883  stonith_callback_data_t data = { 0, };
884 
885  data.call_id = call_id;
887  data.userdata = userdata;
888  data.opaque = (void *) result;
889 
890  callback(st, &data);
891 }
892 
904 static void
905 invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id)
906 {
907  stonith_private_t *private = NULL;
908  stonith_callback_client_t *cb_info = NULL;
910 
911  CRM_CHECK(stonith != NULL, return);
912  CRM_CHECK(stonith->st_private != NULL, return);
913 
914  private = stonith->st_private;
915 
916  if (msg == NULL) {
917  // Fencer didn't reply in time
919  "Fencer accepted request but did not reply in time");
920  CRM_LOG_ASSERT(call_id > 0);
921 
922  } else {
923  // We have the fencer reply
924  if ((crm_element_value_int(msg, F_STONITH_CALLID, &call_id) != 0)
925  || (call_id <= 0)) {
926  crm_log_xml_warn(msg, "Bad fencer reply");
927  }
929  }
930 
931  if (call_id > 0) {
932  cb_info = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
933  call_id);
934  }
935 
936  if ((cb_info != NULL) && (cb_info->callback != NULL)
937  && (pcmk__result_ok(&result) || !(cb_info->only_success))) {
938  crm_trace("Invoking callback %s for call %d",
939  crm_str(cb_info->id), call_id);
940  invoke_fence_action_callback(stonith, call_id, &result,
941  cb_info->user_data, cb_info->callback);
942 
943  } else if ((private->op_callback == NULL) && !pcmk__result_ok(&result)) {
944  crm_warn("Fencing action without registered callback failed: %d (%s%s%s)",
946  pcmk_exec_status_str(result.execution_status),
947  ((result.exit_reason == NULL)? "" : ": "),
948  ((result.exit_reason == NULL)? "" : result.exit_reason));
949  crm_log_xml_debug(msg, "Failed fence update");
950  }
951 
952  if (private->op_callback != NULL) {
953  crm_trace("Invoking global callback for call %d", call_id);
954  invoke_fence_action_callback(stonith, call_id, &result, NULL,
955  private->op_callback);
956  }
957 
958  if (cb_info != NULL) {
959  stonith_api_del_callback(stonith, call_id, FALSE);
960  }
962 }
963 
964 static gboolean
965 stonith_async_timeout_handler(gpointer data)
966 {
967  struct timer_rec_s *timer = data;
968 
969  crm_err("Async call %d timed out after %dms", timer->call_id, timer->timeout);
970  invoke_registered_callbacks(timer->stonith, NULL, timer->call_id);
971 
972  /* Always return TRUE, never remove the handler
973  * We do that in stonith_del_callback()
974  */
975  return TRUE;
976 }
977 
978 static void
979 set_callback_timeout(stonith_callback_client_t * callback, stonith_t * stonith, int call_id,
980  int timeout)
981 {
982  struct timer_rec_s *async_timer = callback->timer;
983 
984  if (timeout <= 0) {
985  return;
986  }
987 
988  if (!async_timer) {
989  async_timer = calloc(1, sizeof(struct timer_rec_s));
990  callback->timer = async_timer;
991  }
992 
993  async_timer->stonith = stonith;
994  async_timer->call_id = call_id;
995  /* Allow a fair bit of grace to allow the server to tell us of a timeout
996  * This is only a fallback
997  */
998  async_timer->timeout = (timeout + 60) * 1000;
999  if (async_timer->ref) {
1000  g_source_remove(async_timer->ref);
1001  }
1002  async_timer->ref =
1003  g_timeout_add(async_timer->timeout, stonith_async_timeout_handler, async_timer);
1004 }
1005 
1006 static void
1007 update_callback_timeout(int call_id, int timeout, stonith_t * st)
1008 {
1009  stonith_callback_client_t *callback = NULL;
1010  stonith_private_t *private = st->st_private;
1011 
1012  callback = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
1013  call_id);
1014  if (!callback || !callback->allow_timeout_updates) {
1015  return;
1016  }
1017 
1018  set_callback_timeout(callback, st, call_id, timeout);
1019 }
1020 
1021 static int
1022 stonith_dispatch_internal(const char *buffer, ssize_t length, gpointer userdata)
1023 {
1024  const char *type = NULL;
1025  struct notify_blob_s blob;
1026 
1027  stonith_t *st = userdata;
1028  stonith_private_t *private = NULL;
1029 
1030  CRM_ASSERT(st != NULL);
1031  private = st->st_private;
1032 
1033  blob.stonith = st;
1034  blob.xml = string2xml(buffer);
1035  if (blob.xml == NULL) {
1036  crm_warn("Received malformed message from fencer: %s", buffer);
1037  return 0;
1038  }
1039 
1040  /* do callbacks */
1041  type = crm_element_value(blob.xml, F_TYPE);
1042  crm_trace("Activating %s callbacks...", type);
1043 
1044  if (pcmk__str_eq(type, T_STONITH_NG, pcmk__str_casei)) {
1045  invoke_registered_callbacks(st, blob.xml, 0);
1046 
1047  } else if (pcmk__str_eq(type, T_STONITH_NOTIFY, pcmk__str_casei)) {
1048  foreach_notify_entry(private, stonith_send_notification, &blob);
1049  } else if (pcmk__str_eq(type, T_STONITH_TIMEOUT_VALUE, pcmk__str_casei)) {
1050  int call_id = 0;
1051  int timeout = 0;
1052 
1054  crm_element_value_int(blob.xml, F_STONITH_CALLID, &call_id);
1055 
1056  update_callback_timeout(call_id, timeout, st);
1057  } else {
1058  crm_err("Unknown message type: %s", type);
1059  crm_log_xml_warn(blob.xml, "BadReply");
1060  }
1061 
1062  free_xml(blob.xml);
1063  return 1;
1064 }
1065 
1066 static int
1067 stonith_api_signon(stonith_t * stonith, const char *name, int *stonith_fd)
1068 {
1069  int rc = pcmk_ok;
1070  stonith_private_t *native = NULL;
1071  const char *display_name = name? name : "client";
1072 
1073  struct ipc_client_callbacks st_callbacks = {
1074  .dispatch = stonith_dispatch_internal,
1075  .destroy = stonith_connection_destroy
1076  };
1077 
1078  CRM_CHECK(stonith != NULL, return -EINVAL);
1079 
1080  native = stonith->st_private;
1081  CRM_ASSERT(native != NULL);
1082 
1083  crm_debug("Attempting fencer connection by %s with%s mainloop",
1084  display_name, (stonith_fd? "out" : ""));
1085 
1086  stonith->state = stonith_connected_command;
1087  if (stonith_fd) {
1088  /* No mainloop */
1089  native->ipc = crm_ipc_new("stonith-ng", 0);
1090 
1091  if (native->ipc && crm_ipc_connect(native->ipc)) {
1092  *stonith_fd = crm_ipc_get_fd(native->ipc);
1093  } else if (native->ipc) {
1094  crm_ipc_close(native->ipc);
1095  crm_ipc_destroy(native->ipc);
1096  native->ipc = NULL;
1097  }
1098 
1099  } else {
1100  /* With mainloop */
1101  native->source =
1102  mainloop_add_ipc_client("stonith-ng", G_PRIORITY_MEDIUM, 0, stonith, &st_callbacks);
1103  native->ipc = mainloop_get_ipc_client(native->source);
1104  }
1105 
1106  if (native->ipc == NULL) {
1107  rc = -ENOTCONN;
1108  } else {
1109  xmlNode *reply = NULL;
1110  xmlNode *hello = create_xml_node(NULL, "stonith_command");
1111 
1112  crm_xml_add(hello, F_TYPE, T_STONITH_NG);
1115  rc = crm_ipc_send(native->ipc, hello, crm_ipc_client_response, -1, &reply);
1116 
1117  if (rc < 0) {
1118  crm_debug("Couldn't register with the fencer: %s "
1119  CRM_XS " rc=%d", pcmk_strerror(rc), rc);
1120  rc = -ECOMM;
1121 
1122  } else if (reply == NULL) {
1123  crm_debug("Couldn't register with the fencer: no reply");
1124  rc = -EPROTO;
1125 
1126  } else {
1127  const char *msg_type = crm_element_value(reply, F_STONITH_OPERATION);
1128 
1129  native->token = crm_element_value_copy(reply, F_STONITH_CLIENTID);
1130  if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_casei)) {
1131  crm_debug("Couldn't register with the fencer: invalid reply type '%s'",
1132  (msg_type? msg_type : "(missing)"));
1133  crm_log_xml_debug(reply, "Invalid fencer reply");
1134  rc = -EPROTO;
1135 
1136  } else if (native->token == NULL) {
1137  crm_debug("Couldn't register with the fencer: no token in reply");
1138  crm_log_xml_debug(reply, "Invalid fencer reply");
1139  rc = -EPROTO;
1140 
1141  } else {
1142  crm_debug("Connection to fencer by %s succeeded (registration token: %s)",
1143  display_name, native->token);
1144  rc = pcmk_ok;
1145  }
1146  }
1147 
1148  free_xml(reply);
1149  free_xml(hello);
1150  }
1151 
1152  if (rc != pcmk_ok) {
1153  crm_debug("Connection attempt to fencer by %s failed: %s "
1154  CRM_XS " rc=%d", display_name, pcmk_strerror(rc), rc);
1155  stonith->cmds->disconnect(stonith);
1156  }
1157  return rc;
1158 }
1159 
1160 static int
1161 stonith_set_notification(stonith_t * stonith, const char *callback, int enabled)
1162 {
1163  int rc = pcmk_ok;
1164  xmlNode *notify_msg = create_xml_node(NULL, __func__);
1165  stonith_private_t *native = stonith->st_private;
1166 
1167  if (stonith->state != stonith_disconnected) {
1168 
1170  if (enabled) {
1171  crm_xml_add(notify_msg, F_STONITH_NOTIFY_ACTIVATE, callback);
1172  } else {
1173  crm_xml_add(notify_msg, F_STONITH_NOTIFY_DEACTIVATE, callback);
1174  }
1175 
1176  rc = crm_ipc_send(native->ipc, notify_msg, crm_ipc_client_response, -1, NULL);
1177  if (rc < 0) {
1178  crm_perror(LOG_DEBUG, "Couldn't register for fencing notifications: %d", rc);
1179  rc = -ECOMM;
1180  } else {
1181  rc = pcmk_ok;
1182  }
1183  }
1184 
1185  free_xml(notify_msg);
1186  return rc;
1187 }
1188 
1189 static int
1190 stonith_api_add_notification(stonith_t * stonith, const char *event,
1191  void (*callback) (stonith_t * stonith, stonith_event_t * e))
1192 {
1193  GList *list_item = NULL;
1194  stonith_notify_client_t *new_client = NULL;
1195  stonith_private_t *private = NULL;
1196 
1197  private = stonith->st_private;
1198  crm_trace("Adding callback for %s events (%d)", event, g_list_length(private->notify_list));
1199 
1200  new_client = calloc(1, sizeof(stonith_notify_client_t));
1201  new_client->event = event;
1202  new_client->notify = callback;
1203 
1204  list_item = g_list_find_custom(private->notify_list, new_client, stonithlib_GCompareFunc);
1205 
1206  if (list_item != NULL) {
1207  crm_warn("Callback already present");
1208  free(new_client);
1209  return -ENOTUNIQ;
1210 
1211  } else {
1212  private->notify_list = g_list_append(private->notify_list, new_client);
1213 
1214  stonith_set_notification(stonith, event, 1);
1215 
1216  crm_trace("Callback added (%d)", g_list_length(private->notify_list));
1217  }
1218  return pcmk_ok;
1219 }
1220 
1221 static void
1222 del_notify_entry(gpointer data, gpointer user_data)
1223 {
1224  stonith_notify_client_t *entry = data;
1225  stonith_t * stonith = user_data;
1226 
1227  if (!entry->delete) {
1228  crm_debug("Removing callback for %s events", entry->event);
1229  stonith_api_del_notification(stonith, entry->event);
1230  }
1231 }
1232 
1233 static int
1234 stonith_api_del_notification(stonith_t * stonith, const char *event)
1235 {
1236  GList *list_item = NULL;
1237  stonith_notify_client_t *new_client = NULL;
1238  stonith_private_t *private = stonith->st_private;
1239 
1240  if (event == NULL) {
1241  foreach_notify_entry(private, del_notify_entry, stonith);
1242  crm_trace("Removed callback");
1243 
1244  return pcmk_ok;
1245  }
1246 
1247  crm_debug("Removing callback for %s events", event);
1248 
1249  new_client = calloc(1, sizeof(stonith_notify_client_t));
1250  new_client->event = event;
1251  new_client->notify = NULL;
1252 
1253  list_item = g_list_find_custom(private->notify_list, new_client, stonithlib_GCompareFunc);
1254 
1255  stonith_set_notification(stonith, event, 0);
1256 
1257  if (list_item != NULL) {
1258  stonith_notify_client_t *list_client = list_item->data;
1259 
1260  if (private->notify_refcnt) {
1261  list_client->delete = TRUE;
1262  private->notify_deletes = TRUE;
1263  } else {
1264  private->notify_list = g_list_remove(private->notify_list, list_client);
1265  free(list_client);
1266  }
1267 
1268  crm_trace("Removed callback");
1269 
1270  } else {
1271  crm_trace("Callback not present");
1272  }
1273  free(new_client);
1274  return pcmk_ok;
1275 }
1276 
1277 static int
1278 stonith_api_add_callback(stonith_t * stonith, int call_id, int timeout, int options,
1279  void *user_data, const char *callback_name,
1280  void (*callback) (stonith_t * st, stonith_callback_data_t * data))
1281 {
1282  stonith_callback_client_t *blob = NULL;
1283  stonith_private_t *private = NULL;
1284 
1285  CRM_CHECK(stonith != NULL, return -EINVAL);
1286  CRM_CHECK(stonith->st_private != NULL, return -EINVAL);
1287  private = stonith->st_private;
1288 
1289  if (call_id == 0) { // Add global callback
1290  private->op_callback = callback;
1291 
1292  } else if (call_id < 0) { // Call failed immediately, so call callback now
1293  if (!(options & st_opt_report_only_success)) {
1295 
1296  crm_trace("Call failed, calling %s: %s", callback_name, pcmk_strerror(call_id));
1298  stonith__legacy2status(call_id), NULL);
1299  invoke_fence_action_callback(stonith, call_id, &result,
1300  user_data, callback);
1301  } else {
1302  crm_warn("Fencer call failed: %s", pcmk_strerror(call_id));
1303  }
1304  return FALSE;
1305  }
1306 
1307  blob = calloc(1, sizeof(stonith_callback_client_t));
1308  blob->id = callback_name;
1309  blob->only_success = (options & st_opt_report_only_success) ? TRUE : FALSE;
1310  blob->user_data = user_data;
1311  blob->callback = callback;
1312  blob->allow_timeout_updates = (options & st_opt_timeout_updates) ? TRUE : FALSE;
1313 
1314  if (timeout > 0) {
1315  set_callback_timeout(blob, stonith, call_id, timeout);
1316  }
1317 
1318  pcmk__intkey_table_insert(private->stonith_op_callback_table, call_id,
1319  blob);
1320  crm_trace("Added callback to %s for call %d", callback_name, call_id);
1321 
1322  return TRUE;
1323 }
1324 
1325 static void
1326 stonith_dump_pending_op(gpointer key, gpointer value, gpointer user_data)
1327 {
1328  int call = GPOINTER_TO_INT(key);
1329  stonith_callback_client_t *blob = value;
1330 
1331  crm_debug("Call %d (%s): pending", call, crm_str(blob->id));
1332 }
1333 
1334 void
1336 {
1337  stonith_private_t *private = stonith->st_private;
1338 
1339  if (private->stonith_op_callback_table == NULL) {
1340  return;
1341  }
1342  return g_hash_table_foreach(private->stonith_op_callback_table, stonith_dump_pending_op, NULL);
1343 }
1344 
1352 static xmlNode *
1353 get_event_data_xml(xmlNode *msg, const char *ntype)
1354 {
1355  char *data_addr = crm_strdup_printf("//%s", ntype);
1356  xmlNode *data = get_xpath_object(data_addr, msg, LOG_DEBUG);
1357 
1358  free(data_addr);
1359  return data;
1360 }
1361 
1362 /*
1363  <notify t="st_notify" subt="st_device_register" st_op="st_device_register" st_rc="0" >
1364  <st_calldata >
1365  <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" >
1366  <st_calldata >
1367  <st_device_id id="test-id" origin="create_device_registration_xml" agent="fence_virsh" namespace="stonith-ng" >
1368  <attributes ipaddr="localhost" pcmk-portmal="some-host=pcmk-1 pcmk-3=3,4" login="root" identity_file="/root/.ssh/id_dsa" />
1369  </st_device_id>
1370  </st_calldata>
1371  </stonith_command>
1372  </st_calldata>
1373  </notify>
1374 
1375  <notify t="st_notify" subt="st_notify_fence" st_op="st_notify_fence" st_rc="0" >
1376  <st_calldata >
1377  <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" />
1378  </st_calldata>
1379  </notify>
1380 */
1381 static stonith_event_t *
1382 xml_to_event(xmlNode *msg)
1383 {
1384  stonith_event_t *event = calloc(1, sizeof(stonith_event_t));
1385  const char *ntype = crm_element_value(msg, F_SUBTYPE);
1386  struct event_private *event_private = NULL;
1387 
1388  CRM_ASSERT(event != NULL);
1389 
1390  event->opaque = calloc(1, sizeof(struct event_private));
1391  CRM_ASSERT(event->opaque != NULL);
1392  event_private = (struct event_private *) event->opaque;
1393 
1394  crm_log_xml_trace(msg, "stonith_notify");
1395 
1396  // All notification types have the operation result
1397  stonith__xe_get_result(msg, &event_private->result);
1398 
1399  // @COMPAT The API originally provided the result as a legacy return code
1400  event->result = pcmk_rc2legacy(stonith__result2rc(&event_private->result));
1401 
1402  // Fence notifications have additional information
1403  if (pcmk__str_eq(ntype, T_STONITH_NOTIFY_FENCE, pcmk__str_casei)) {
1404  xmlNode *data = get_event_data_xml(msg, ntype);
1405 
1406  if (data == NULL) {
1407  crm_err("No data for %s event", ntype);
1408  crm_log_xml_notice(msg, "BadEvent");
1409  } else {
1410  event->origin = crm_element_value_copy(data, F_STONITH_ORIGIN);
1411  event->action = crm_element_value_copy(data, F_STONITH_ACTION);
1412  event->target = crm_element_value_copy(data, F_STONITH_TARGET);
1413  event->executioner = crm_element_value_copy(data, F_STONITH_DELEGATE);
1415  event->client_origin = crm_element_value_copy(data, F_STONITH_CLIENTNAME);
1416  event->device = crm_element_value_copy(data, F_STONITH_DEVICE);
1417  }
1418  event->operation = crm_element_value_copy(msg, F_STONITH_OPERATION);
1419  }
1420 
1421  return event;
1422 }
1423 
1424 static void
1425 event_free(stonith_event_t * event)
1426 {
1427  struct event_private *event_private = event->opaque;
1428 
1429  free(event->id);
1430  free(event->type);
1431  free(event->message);
1432  free(event->operation);
1433  free(event->origin);
1434  free(event->action);
1435  free(event->target);
1436  free(event->executioner);
1437  free(event->device);
1438  free(event->client_origin);
1439  pcmk__reset_result(&event_private->result);
1440  free(event->opaque);
1441  free(event);
1442 }
1443 
1444 static void
1445 stonith_send_notification(gpointer data, gpointer user_data)
1446 {
1447  struct notify_blob_s *blob = user_data;
1448  stonith_notify_client_t *entry = data;
1449  stonith_event_t *st_event = NULL;
1450  const char *event = NULL;
1451 
1452  if (blob->xml == NULL) {
1453  crm_warn("Skipping callback - NULL message");
1454  return;
1455  }
1456 
1457  event = crm_element_value(blob->xml, F_SUBTYPE);
1458 
1459  if (entry == NULL) {
1460  crm_warn("Skipping callback - NULL callback client");
1461  return;
1462 
1463  } else if (entry->delete) {
1464  crm_trace("Skipping callback - marked for deletion");
1465  return;
1466 
1467  } else if (entry->notify == NULL) {
1468  crm_warn("Skipping callback - NULL callback");
1469  return;
1470 
1471  } else if (!pcmk__str_eq(entry->event, event, pcmk__str_casei)) {
1472  crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event);
1473  return;
1474  }
1475 
1476  st_event = xml_to_event(blob->xml);
1477 
1478  crm_trace("Invoking callback for %p/%s event...", entry, event);
1479  entry->notify(blob->stonith, st_event);
1480  crm_trace("Callback invoked...");
1481 
1482  event_free(st_event);
1483 }
1484 
1499 static int
1500 stonith_send_command(stonith_t * stonith, const char *op, xmlNode * data, xmlNode ** output_data,
1501  int call_options, int timeout)
1502 {
1503  int rc = 0;
1504  int reply_id = -1;
1505 
1506  xmlNode *op_msg = NULL;
1507  xmlNode *op_reply = NULL;
1508  stonith_private_t *native = NULL;
1509 
1510  CRM_ASSERT(stonith && stonith->st_private && op);
1511  native = stonith->st_private;
1512 
1513  if (output_data != NULL) {
1514  *output_data = NULL;
1515  }
1516 
1517  if ((stonith->state == stonith_disconnected) || (native->token == NULL)) {
1518  return -ENOTCONN;
1519  }
1520 
1521  /* Increment the call ID, which must be positive to avoid conflicting with
1522  * error codes. This shouldn't be a problem unless the client mucked with
1523  * it or the counter wrapped around.
1524  */
1525  stonith->call_id++;
1526  if (stonith->call_id < 1) {
1527  stonith->call_id = 1;
1528  }
1529 
1530  op_msg = stonith_create_op(stonith->call_id, native->token, op, data, call_options);
1531  if (op_msg == NULL) {
1532  return -EINVAL;
1533  }
1534 
1536  crm_trace("Sending %s message to fencer with timeout %ds", op, timeout);
1537 
1538  if (data) {
1539  const char *delay_s = crm_element_value(data, F_STONITH_DELAY);
1540 
1541  if (delay_s) {
1542  crm_xml_add(op_msg, F_STONITH_DELAY, delay_s);
1543  }
1544  }
1545 
1546  {
1547  enum crm_ipc_flags ipc_flags = crm_ipc_flags_none;
1548 
1549  if (call_options & st_opt_sync_call) {
1550  pcmk__set_ipc_flags(ipc_flags, "stonith command",
1552  }
1553  rc = crm_ipc_send(native->ipc, op_msg, ipc_flags,
1554  1000 * (timeout + 60), &op_reply);
1555  }
1556  free_xml(op_msg);
1557 
1558  if (rc < 0) {
1559  crm_perror(LOG_ERR, "Couldn't perform %s operation (timeout=%ds): %d", op, timeout, rc);
1560  rc = -ECOMM;
1561  goto done;
1562  }
1563 
1564  crm_log_xml_trace(op_reply, "Reply");
1565 
1566  if (!(call_options & st_opt_sync_call)) {
1567  crm_trace("Async call %d, returning", stonith->call_id);
1568  free_xml(op_reply);
1569  return stonith->call_id;
1570  }
1571 
1572  rc = pcmk_ok;
1573  crm_element_value_int(op_reply, F_STONITH_CALLID, &reply_id);
1574 
1575  if (reply_id == stonith->call_id) {
1577 
1578  crm_trace("Synchronous reply %d received", reply_id);
1579 
1580  stonith__xe_get_result(op_reply, &result);
1583 
1584  if ((call_options & st_opt_discard_reply) || output_data == NULL) {
1585  crm_trace("Discarding reply");
1586 
1587  } else {
1588  *output_data = op_reply;
1589  op_reply = NULL; /* Prevent subsequent free */
1590  }
1591 
1592  } else if (reply_id <= 0) {
1593  crm_err("Received bad reply: No id set");
1594  crm_log_xml_err(op_reply, "Bad reply");
1595  free_xml(op_reply);
1596  rc = -ENOMSG;
1597 
1598  } else {
1599  crm_err("Received bad reply: %d (wanted %d)", reply_id, stonith->call_id);
1600  crm_log_xml_err(op_reply, "Old reply");
1601  free_xml(op_reply);
1602  rc = -ENOMSG;
1603  }
1604 
1605  done:
1606  if (crm_ipc_connected(native->ipc) == FALSE) {
1607  crm_err("Fencer disconnected");
1608  free(native->token); native->token = NULL;
1609  stonith->state = stonith_disconnected;
1610  }
1611 
1612  free_xml(op_reply);
1613  return rc;
1614 }
1615 
1616 /* Not used with mainloop */
1617 bool
1619 {
1620  gboolean stay_connected = TRUE;
1621  stonith_private_t *private = NULL;
1622 
1623  CRM_ASSERT(st != NULL);
1624  private = st->st_private;
1625 
1626  while (crm_ipc_ready(private->ipc)) {
1627 
1628  if (crm_ipc_read(private->ipc) > 0) {
1629  const char *msg = crm_ipc_buffer(private->ipc);
1630 
1631  stonith_dispatch_internal(msg, strlen(msg), st);
1632  }
1633 
1634  if (crm_ipc_connected(private->ipc) == FALSE) {
1635  crm_err("Connection closed");
1636  stay_connected = FALSE;
1637  }
1638  }
1639 
1640  return stay_connected;
1641 }
1642 
1643 static int
1644 stonith_api_free(stonith_t * stonith)
1645 {
1646  int rc = pcmk_ok;
1647 
1648  crm_trace("Destroying %p", stonith);
1649 
1650  if (stonith->state != stonith_disconnected) {
1651  crm_trace("Disconnecting %p first", stonith);
1652  rc = stonith->cmds->disconnect(stonith);
1653  }
1654 
1655  if (stonith->state == stonith_disconnected) {
1656  stonith_private_t *private = stonith->st_private;
1657 
1658  crm_trace("Removing %d callbacks", g_hash_table_size(private->stonith_op_callback_table));
1659  g_hash_table_destroy(private->stonith_op_callback_table);
1660 
1661  crm_trace("Destroying %d notification clients", g_list_length(private->notify_list));
1662  g_list_free_full(private->notify_list, free);
1663 
1664  free(stonith->st_private);
1665  free(stonith->cmds);
1666  free(stonith);
1667 
1668  } else {
1669  crm_err("Not free'ing active connection: %s (%d)", pcmk_strerror(rc), rc);
1670  }
1671 
1672  return rc;
1673 }
1674 
1675 void
1677 {
1678  crm_trace("Destroying %p", stonith);
1679  if(stonith) {
1680  stonith->cmds->free(stonith);
1681  }
1682 }
1683 
1684 static int
1685 stonith_api_validate(stonith_t *st, int call_options, const char *rsc_id,
1686  const char *namespace_s, const char *agent,
1687  stonith_key_value_t *params, int timeout, char **output,
1688  char **error_output)
1689 {
1690  /* Validation should be done directly via the agent, so we can get it from
1691  * stonith_admin when the cluster is not running, which is important for
1692  * higher-level tools.
1693  */
1694 
1695  int rc = pcmk_ok;
1696 
1697  /* Use a dummy node name in case the agent requires a target. We assume the
1698  * actual target doesn't matter for validation purposes (if in practice,
1699  * that is incorrect, we will need to allow the caller to pass the target).
1700  */
1701  const char *target = "node1";
1702  const char *host_arg = NULL;
1703 
1704  GHashTable *params_table = pcmk__strkey_table(free, free);
1705 
1706  // Convert parameter list to a hash table
1707  for (; params; params = params->next) {
1708  if (pcmk__str_eq(params->key, PCMK_STONITH_HOST_ARGUMENT,
1709  pcmk__str_casei)) {
1710  host_arg = params->value;
1711  }
1712  if (!pcmk_stonith_param(params->key)) {
1713  g_hash_table_insert(params_table, strdup(params->key),
1714  strdup(params->value));
1715  }
1716  }
1717 
1718 #if SUPPORT_CIBSECRETS
1719  rc = pcmk__substitute_secrets(rsc_id, params_table);
1720  if (rc != pcmk_rc_ok) {
1721  crm_warn("Could not replace secret parameters for validation of %s: %s",
1722  agent, pcmk_rc_str(rc));
1723  // rc is standard return value, don't return it in this function
1724  }
1725 #endif
1726 
1727  if (output) {
1728  *output = NULL;
1729  }
1730  if (error_output) {
1731  *error_output = NULL;
1732  }
1733 
1734  switch (stonith_get_namespace(agent, namespace_s)) {
1735  case st_namespace_rhcs:
1736  rc = stonith__rhcs_validate(st, call_options, target, agent,
1737  params_table, host_arg, timeout,
1738  output, error_output);
1739  break;
1740 
1741 #if HAVE_STONITH_STONITH_H
1742  case st_namespace_lha:
1743  rc = stonith__lha_validate(st, call_options, target, agent,
1744  params_table, timeout, output,
1745  error_output);
1746  break;
1747 #endif
1748 
1749  default:
1750  rc = -EINVAL;
1751  errno = EINVAL;
1752  crm_perror(LOG_ERR,
1753  "Agent %s not found or does not support validation",
1754  agent);
1755  break;
1756  }
1757  g_hash_table_destroy(params_table);
1758  return rc;
1759 }
1760 
1761 stonith_t *
1763 {
1764  stonith_t *new_stonith = NULL;
1765  stonith_private_t *private = NULL;
1766 
1767  new_stonith = calloc(1, sizeof(stonith_t));
1768  if (new_stonith == NULL) {
1769  return NULL;
1770  }
1771 
1772  private = calloc(1, sizeof(stonith_private_t));
1773  if (private == NULL) {
1774  free(new_stonith);
1775  return NULL;
1776  }
1777  new_stonith->st_private = private;
1778 
1779  private->stonith_op_callback_table = pcmk__intkey_table(stonith_destroy_op_callback);
1780  private->notify_list = NULL;
1781  private->notify_refcnt = 0;
1782  private->notify_deletes = FALSE;
1783 
1784  new_stonith->call_id = 1;
1785  new_stonith->state = stonith_disconnected;
1786 
1787  new_stonith->cmds = calloc(1, sizeof(stonith_api_operations_t));
1788  if (new_stonith->cmds == NULL) {
1789  free(new_stonith->st_private);
1790  free(new_stonith);
1791  return NULL;
1792  }
1793 
1794 /* *INDENT-OFF* */
1795  new_stonith->cmds->free = stonith_api_free;
1796  new_stonith->cmds->connect = stonith_api_signon;
1797  new_stonith->cmds->disconnect = stonith_api_signoff;
1798 
1799  new_stonith->cmds->list = stonith_api_list;
1800  new_stonith->cmds->monitor = stonith_api_monitor;
1801  new_stonith->cmds->status = stonith_api_status;
1802  new_stonith->cmds->fence = stonith_api_fence;
1803  new_stonith->cmds->fence_with_delay = stonith_api_fence_with_delay;
1804  new_stonith->cmds->confirm = stonith_api_confirm;
1805  new_stonith->cmds->history = stonith_api_history;
1806 
1807  new_stonith->cmds->list_agents = stonith_api_device_list;
1808  new_stonith->cmds->metadata = stonith_api_device_metadata;
1809 
1810  new_stonith->cmds->query = stonith_api_query;
1811  new_stonith->cmds->remove_device = stonith_api_remove_device;
1812  new_stonith->cmds->register_device = stonith_api_register_device;
1813 
1814  new_stonith->cmds->remove_level = stonith_api_remove_level;
1815  new_stonith->cmds->remove_level_full = stonith_api_remove_level_full;
1816  new_stonith->cmds->register_level = stonith_api_register_level;
1817  new_stonith->cmds->register_level_full = stonith_api_register_level_full;
1818 
1819  new_stonith->cmds->remove_callback = stonith_api_del_callback;
1820  new_stonith->cmds->register_callback = stonith_api_add_callback;
1821  new_stonith->cmds->remove_notification = stonith_api_del_notification;
1822  new_stonith->cmds->register_notification = stonith_api_add_notification;
1823 
1824  new_stonith->cmds->validate = stonith_api_validate;
1825 /* *INDENT-ON* */
1826 
1827  return new_stonith;
1828 }
1829 
1839 int
1840 stonith_api_connect_retry(stonith_t *st, const char *name, int max_attempts)
1841 {
1842  int rc = -EINVAL; // if max_attempts is not positive
1843 
1844  for (int attempt = 1; attempt <= max_attempts; attempt++) {
1845  rc = st->cmds->connect(st, name, NULL);
1846  if (rc == pcmk_ok) {
1847  return pcmk_ok;
1848  } else if (attempt < max_attempts) {
1849  crm_notice("Fencer connection attempt %d of %d failed (retrying in 2s): %s "
1850  CRM_XS " rc=%d",
1851  attempt, max_attempts, pcmk_strerror(rc), rc);
1852  sleep(2);
1853  }
1854  }
1855  crm_notice("Could not connect to fencer: %s " CRM_XS " rc=%d",
1856  pcmk_strerror(rc), rc);
1857  return rc;
1858 }
1859 
1861 stonith_key_value_add(stonith_key_value_t * head, const char *key, const char *value)
1862 {
1863  stonith_key_value_t *p, *end;
1864 
1865  p = calloc(1, sizeof(stonith_key_value_t));
1866  pcmk__str_update(&p->key, key);
1867  pcmk__str_update(&p->value, value);
1868 
1869  end = head;
1870  while (end && end->next) {
1871  end = end->next;
1872  }
1873 
1874  if (end) {
1875  end->next = p;
1876  } else {
1877  head = p;
1878  }
1879 
1880  return head;
1881 }
1882 
1883 void
1884 stonith_key_value_freeall(stonith_key_value_t * head, int keys, int values)
1885 {
1887 
1888  while (head) {
1889  p = head->next;
1890  if (keys) {
1891  free(head->key);
1892  }
1893  if (values) {
1894  free(head->value);
1895  }
1896  free(head);
1897  head = p;
1898  }
1899 }
1900 
1901 #define api_log_open() openlog("stonith-api", LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON)
1902 #define api_log(level, fmt, args...) syslog(level, "%s: "fmt, __func__, args)
1903 
1904 int
1905 stonith_api_kick(uint32_t nodeid, const char *uname, int timeout, bool off)
1906 {
1907  int rc = pcmk_ok;
1909  const char *action = off? "off" : "reboot";
1910 
1911  api_log_open();
1912  if (st == NULL) {
1913  api_log(LOG_ERR, "API initialization failed, could not kick (%s) node %u/%s",
1914  action, nodeid, uname);
1915  return -EPROTO;
1916  }
1917 
1918  rc = st->cmds->connect(st, "stonith-api", NULL);
1919  if (rc != pcmk_ok) {
1920  api_log(LOG_ERR, "Connection failed, could not kick (%s) node %u/%s : %s (%d)",
1921  action, nodeid, uname, pcmk_strerror(rc), rc);
1922  } else {
1923  char *name = (uname == NULL)? pcmk__itoa(nodeid) : strdup(uname);
1924  int opts = 0;
1925 
1928  if ((uname == NULL) && (nodeid > 0)) {
1930  }
1931  rc = st->cmds->fence(st, opts, name, action, timeout, 0);
1932  free(name);
1933 
1934  if (rc != pcmk_ok) {
1935  api_log(LOG_ERR, "Could not kick (%s) node %u/%s : %s (%d)",
1936  action, nodeid, uname, pcmk_strerror(rc), rc);
1937  } else {
1938  api_log(LOG_NOTICE, "Node %u/%s kicked: %s", nodeid, uname, action);
1939  }
1940  }
1941 
1943  return rc;
1944 }
1945 
1946 time_t
1947 stonith_api_time(uint32_t nodeid, const char *uname, bool in_progress)
1948 {
1949  int rc = pcmk_ok;
1950  time_t when = 0;
1952  stonith_history_t *history = NULL, *hp = NULL;
1953 
1954  if (st == NULL) {
1955  api_log(LOG_ERR, "Could not retrieve fence history for %u/%s: "
1956  "API initialization failed", nodeid, uname);
1957  return when;
1958  }
1959 
1960  rc = st->cmds->connect(st, "stonith-api", NULL);
1961  if (rc != pcmk_ok) {
1962  api_log(LOG_NOTICE, "Connection failed: %s (%d)", pcmk_strerror(rc), rc);
1963  } else {
1964  int entries = 0;
1965  int progress = 0;
1966  int completed = 0;
1967  int opts = 0;
1968  char *name = (uname == NULL)? pcmk__itoa(nodeid) : strdup(uname);
1969 
1971  if ((uname == NULL) && (nodeid > 0)) {
1973  }
1974  rc = st->cmds->history(st, opts, name, &history, 120);
1975  free(name);
1976 
1977  for (hp = history; hp; hp = hp->next) {
1978  entries++;
1979  if (in_progress) {
1980  progress++;
1981  if (hp->state != st_done && hp->state != st_failed) {
1982  when = time(NULL);
1983  }
1984 
1985  } else if (hp->state == st_done) {
1986  completed++;
1987  if (hp->completed > when) {
1988  when = hp->completed;
1989  }
1990  }
1991  }
1992 
1993  stonith_history_free(history);
1994 
1995  if(rc == pcmk_ok) {
1996  api_log(LOG_INFO, "Found %d entries for %u/%s: %d in progress, %d completed", entries, nodeid, uname, progress, completed);
1997  } else {
1998  api_log(LOG_ERR, "Could not retrieve fence history for %u/%s: %s (%d)", nodeid, uname, pcmk_strerror(rc), rc);
1999  }
2000  }
2001 
2003 
2004  if(when) {
2005  api_log(LOG_INFO, "Node %u/%s last kicked at: %ld", nodeid, uname, (long int)when);
2006  }
2007  return when;
2008 }
2009 
2010 bool
2011 stonith_agent_exists(const char *agent, int timeout)
2012 {
2013  stonith_t *st = NULL;
2014  stonith_key_value_t *devices = NULL;
2015  stonith_key_value_t *dIter = NULL;
2016  bool rc = FALSE;
2017 
2018  if (agent == NULL) {
2019  return rc;
2020  }
2021 
2022  st = stonith_api_new();
2023  if (st == NULL) {
2024  crm_err("Could not list fence agents: API memory allocation failed");
2025  return FALSE;
2026  }
2027  st->cmds->list_agents(st, st_opt_sync_call, NULL, &devices, timeout == 0 ? 120 : timeout);
2028 
2029  for (dIter = devices; dIter != NULL; dIter = dIter->next) {
2030  if (pcmk__str_eq(dIter->value, agent, pcmk__str_none)) {
2031  rc = TRUE;
2032  break;
2033  }
2034  }
2035 
2036  stonith_key_value_freeall(devices, 1, 1);
2038  return rc;
2039 }
2040 
2041 const char *
2043 {
2044  if (action == NULL) {
2045  return "fencing";
2046  } else if (!strcmp(action, "on")) {
2047  return "unfencing";
2048  } else if (!strcmp(action, "off")) {
2049  return "turning off";
2050  } else {
2051  return action;
2052  }
2053 }
2054 
2063 static void
2064 parse_list_line(const char *line, int len, GList **output)
2065 {
2066  size_t i = 0;
2067  size_t entry_start = 0;
2068 
2069  /* Skip complaints about additional parameters device doesn't understand
2070  *
2071  * @TODO Document or eliminate the implied restriction of target names
2072  */
2073  if (strstr(line, "invalid") || strstr(line, "variable")) {
2074  crm_debug("Skipping list output line: %s", line);
2075  return;
2076  }
2077 
2078  // Process line content, character by character
2079  for (i = 0; i <= len; i++) {
2080 
2081  if (isspace(line[i]) || (line[i] == ',') || (line[i] == ';')
2082  || (line[i] == '\0')) {
2083  // We've found a separator (i.e. the end of an entry)
2084 
2085  int rc = 0;
2086  char *entry = NULL;
2087 
2088  if (i == entry_start) {
2089  // Skip leading and sequential separators
2090  entry_start = i + 1;
2091  continue;
2092  }
2093 
2094  entry = calloc(i - entry_start + 1, sizeof(char));
2095  CRM_ASSERT(entry != NULL);
2096 
2097  /* Read entry, stopping at first separator
2098  *
2099  * @TODO Document or eliminate these character restrictions
2100  */
2101  rc = sscanf(line + entry_start, "%[a-zA-Z0-9_-.]", entry);
2102  if (rc != 1) {
2103  crm_warn("Could not parse list output entry: %s "
2104  CRM_XS " entry_start=%d position=%d",
2105  line + entry_start, entry_start, i);
2106  free(entry);
2107 
2108  } else if (pcmk__strcase_any_of(entry, "on", "off", NULL)) {
2109  /* Some agents print the target status in the list output,
2110  * though none are known now (the separate list-status command
2111  * is used for this, but it can also print "UNKNOWN"). To handle
2112  * this possibility, skip such entries.
2113  *
2114  * @TODO Document or eliminate the implied restriction of target
2115  * names.
2116  */
2117  free(entry);
2118 
2119  } else {
2120  // We have a valid entry
2121  *output = g_list_append(*output, entry);
2122  }
2123  entry_start = i + 1;
2124  }
2125  }
2126 }
2127 
2149 GList *
2150 stonith__parse_targets(const char *target_spec)
2151 {
2152  GList *targets = NULL;
2153 
2154  if (target_spec != NULL) {
2155  size_t out_len = strlen(target_spec);
2156  size_t line_start = 0; // Starting index of line being processed
2157 
2158  for (size_t i = 0; i <= out_len; ++i) {
2159  if ((target_spec[i] == '\n') || (target_spec[i] == '\0')
2160  || ((target_spec[i] == '\\') && (target_spec[i + 1] == 'n'))) {
2161  // We've reached the end of one line of output
2162 
2163  int len = i - line_start;
2164 
2165  if (len > 0) {
2166  char *line = strndup(target_spec + line_start, len);
2167 
2168  line[len] = '\0'; // Because it might be a newline
2169  parse_list_line(line, len, &targets);
2170  free(line);
2171  }
2172  if (target_spec[i] == '\\') {
2173  ++i; // backslash-n takes up two positions
2174  }
2175  line_start = i + 1;
2176  }
2177  }
2178  }
2179  return targets;
2180 }
2181 
2189 gboolean
2191 {
2192  gboolean ret = FALSE;
2193 
2194  for (stonith_history_t *prev_hp = top_history; prev_hp; prev_hp = prev_hp->next) {
2195  if (prev_hp == event) {
2196  break;
2197  }
2198 
2199  if ((prev_hp->state == st_done) &&
2200  pcmk__str_eq(event->target, prev_hp->target, pcmk__str_casei) &&
2201  pcmk__str_eq(event->action, prev_hp->action, pcmk__str_casei) &&
2202  ((event->completed < prev_hp->completed) ||
2203  ((event->completed == prev_hp->completed) && (event->completed_nsec < prev_hp->completed_nsec)))) {
2204  ret = TRUE;
2205  break;
2206  }
2207  }
2208  return ret;
2209 }
2210 
2222 {
2223  stonith_history_t *new = NULL, *pending = NULL, *hp, *np, *tmp;
2224 
2225  for (hp = history; hp; ) {
2226  tmp = hp->next;
2227  if ((hp->state == st_done) || (hp->state == st_failed)) {
2228  /* sort into new */
2229  if ((!new) || (hp->completed > new->completed) ||
2230  ((hp->completed == new->completed) && (hp->completed_nsec > new->completed_nsec))) {
2231  hp->next = new;
2232  new = hp;
2233  } else {
2234  np = new;
2235  do {
2236  if ((!np->next) || (hp->completed > np->next->completed) ||
2237  ((hp->completed == np->next->completed) && (hp->completed_nsec > np->next->completed_nsec))) {
2238  hp->next = np->next;
2239  np->next = hp;
2240  break;
2241  }
2242  np = np->next;
2243  } while (1);
2244  }
2245  } else {
2246  /* put into pending */
2247  hp->next = pending;
2248  pending = hp;
2249  }
2250  hp = tmp;
2251  }
2252 
2253  /* pending actions don't have a completed-stamp so make them go front */
2254  if (pending) {
2255  stonith_history_t *last_pending = pending;
2256 
2257  while (last_pending->next) {
2258  last_pending = last_pending->next;
2259  }
2260 
2261  last_pending->next = new;
2262  new = pending;
2263  }
2264  return new;
2265 }
2266 
2274 const char *
2276 {
2277  switch (state) {
2278  case st_query: return "querying";
2279  case st_exec: return "executing";
2280  case st_done: return "completed";
2281  case st_duplicate: return "duplicate";
2282  case st_failed: return "failed";
2283  }
2284  return "unknown";
2285 }
2286 
2289  bool (*matching_fn)(stonith_history_t *, void *),
2290  void *user_data)
2291 {
2292  for (stonith_history_t *hp = history; hp; hp = hp->next) {
2293  if (matching_fn(hp, user_data)) {
2294  return hp;
2295  }
2296  }
2297 
2298  return NULL;
2299 }
2300 
2301 bool
2303 {
2304  return history->state != st_failed && history->state != st_done;
2305 }
2306 
2307 bool
2308 stonith__event_state_eq(stonith_history_t *history, void *user_data)
2309 {
2310  return history->state == GPOINTER_TO_INT(user_data);
2311 }
2312 
2313 bool
2315 {
2316  return history->state != GPOINTER_TO_INT(user_data);
2317 }
2318 
2319 void
2320 stonith__device_parameter_flags(uint32_t *device_flags, const char *device_name,
2321  xmlNode *metadata)
2322 {
2323  xmlXPathObjectPtr xpath = NULL;
2324  int max = 0;
2325  int lpc = 0;
2326 
2327  CRM_CHECK((device_flags != NULL) && (metadata != NULL), return);
2328 
2329  xpath = xpath_search(metadata, "//parameter");
2330  max = numXpathResults(xpath);
2331 
2332  if (max <= 0) {
2333  freeXpathObject(xpath);
2334  return;
2335  }
2336 
2337  for (lpc = 0; lpc < max; lpc++) {
2338  const char *parameter = NULL;
2339  xmlNode *match = getXpathResult(xpath, lpc);
2340 
2341  CRM_LOG_ASSERT(match != NULL);
2342  if (match == NULL) {
2343  continue;
2344  }
2345 
2346  parameter = crm_element_value(match, "name");
2347 
2348  if (pcmk__str_eq(parameter, "plug", pcmk__str_casei)) {
2349  stonith__set_device_flags(*device_flags, device_name,
2351 
2352  } else if (pcmk__str_eq(parameter, "port", pcmk__str_casei)) {
2353  stonith__set_device_flags(*device_flags, device_name,
2355  }
2356  }
2357 
2358  freeXpathObject(xpath);
2359 }
2360 
2369 int
2371 {
2372  if ((data == NULL) || (data->opaque == NULL)) {
2373  return CRM_EX_ERROR;
2374  }
2375  return ((pcmk__action_result_t *) data->opaque)->exit_status;
2376 }
2377 
2386 int
2388 {
2389  if ((data == NULL) || (data->opaque == NULL)) {
2390  return PCMK_EXEC_UNKNOWN;
2391  }
2392  return ((pcmk__action_result_t *) data->opaque)->execution_status;
2393 }
2394 
2403 const char *
2405 {
2406  if ((data == NULL) || (data->opaque == NULL)) {
2407  return NULL;
2408  }
2409  return ((pcmk__action_result_t *) data->opaque)->exit_reason;
2410 }
2411 
2420 int
2422 {
2423  if ((event == NULL) || (event->opaque == NULL)) {
2424  return CRM_EX_ERROR;
2425  } else {
2426  struct event_private *event_private = event->opaque;
2427 
2428  return event_private->result.exit_status;
2429  }
2430 }
2431 
2440 int
2442 {
2443  if ((event == NULL) || (event->opaque == NULL)) {
2444  return PCMK_EXEC_UNKNOWN;
2445  } else {
2446  struct event_private *event_private = event->opaque;
2447 
2448  return event_private->result.execution_status;
2449  }
2450 }
2451 
2460 const char *
2462 {
2463  if ((event == NULL) || (event->opaque == NULL)) {
2464  return NULL;
2465  } else {
2466  struct event_private *event_private = event->opaque;
2467 
2468  return event_private->result.exit_reason;
2469  }
2470 }
2471 
2484 char *
2486 {
2487  const char *origin = event->client_origin;
2488  const char *executioner = event->executioner;
2489  const char *reason = stonith__event_exit_reason(event);
2490  const char *status;
2491 
2492  if (origin == NULL) {
2493  origin = "a client";
2494  }
2495  if (executioner == NULL) {
2496  executioner = "the cluster";
2497  }
2498 
2500  status = pcmk_exec_status_str(stonith__event_execution_status(event));
2501  } else if (stonith__event_exit_status(event) != CRM_EX_OK) {
2502  status = pcmk_exec_status_str(PCMK_EXEC_ERROR);
2503  } else {
2504  status = crm_exit_str(CRM_EX_OK);
2505  }
2506 
2507  return crm_strdup_printf("Operation %s of %s by %s for %s@%s: %s%s%s%s (ref=%s)",
2508  event->action, event->target, executioner, origin,
2509  event->origin, status,
2510  ((reason == NULL)? "" : " ("),
2511  ((reason == NULL)? "" : reason),
2512  ((reason == NULL)? "" : ")"),
2513  event->id);
2514 }
2515 
2516 
2517 // Deprecated functions kept only for backward API compatibility
2518 // LCOV_EXCL_START
2519 
2520 const char *get_stonith_provider(const char *agent, const char *provider);
2521 
2522 const char *
2523 get_stonith_provider(const char *agent, const char *provider)
2524 {
2525  return stonith_namespace2text(stonith_get_namespace(agent, provider));
2526 }
2527 
2528 // LCOV_EXCL_STOP
2529 // End deprecated API
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:226
#define XML_ATTR_STONITH_TARGET_ATTRIBUTE
Definition: msg_xml.h:445
void stonith_history_free(stonith_history_t *history)
Definition: st_client.c:737
#define F_STONITH_REMOTE_OP_ID
Definition: internal.h:105
struct stonith_history_s * next
Definition: stonith-ng.h:112
bool crm_ipc_connect(crm_ipc_t *client)
Establish an IPC connection to a Pacemaker component.
Definition: ipc_client.c:847
A dumping ground.
#define F_TYPE
Definition: msg_xml.h:69
#define crm_notice(fmt, args...)
Definition: logging.h:360
int(* register_level_full)(stonith_t *st, int options, const char *node, const char *pattern, const char *attr, const char *value, int level, stonith_key_value_t *device_list)
Register fencing level for specified node, pattern or attribute.
Definition: stonith-ng.h:379
#define F_STONITH_CLIENTID
Definition: internal.h:99
const char * pcmk_strerror(int rc)
Definition: results.c:58
void pcmk__add_separated_word(char **list, size_t *len, const char *word, const char *separator)
Definition: strings.c:703
char data[0]
Definition: cpg.c:55
void stonith__device_parameter_flags(uint32_t *device_flags, const char *device_name, xmlNode *metadata)
Definition: st_client.c:2320
stonith_t * stonith
Definition: st_client.c:79
#define api_log_open()
Definition: st_client.c:1901
int stonith__legacy2status(int rc)
Definition: st_actions.c:379
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition: nvpair.c:782
int pcmk_rc2legacy(int rc)
Definition: results.c:415
bool stonith__event_state_neq(stonith_history_t *history, void *user_data)
Definition: st_client.c:2314
const char * name
Definition: cib.c:24
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:931
stonith_key_value_t * stonith_key_value_add(stonith_key_value_t *head, const char *key, const char *value)
Definition: st_client.c:1861
void stonith_dump_pending_callbacks(stonith_t *stonith)
Definition: st_client.c:1335
int crm_ipc_get_fd(crm_ipc_t *client)
Definition: ipc_client.c:956
#define F_STONITH_DATE_NSEC
Definition: internal.h:141
#define stonith__set_device_flags(device_flags, device_id, flags_to_set)
Definition: internal.h:28
int(* query)(stonith_t *st, int options, const char *node, stonith_key_value_t **devices, int timeout)
Retrieve a list of registered stonith devices.
Definition: stonith-ng.h:262
xmlNode * create_device_registration_xml(const char *id, enum stonith_namespace namespace, const char *agent, stonith_key_value_t *params, const char *rsc_provides)
Definition: st_client.c:293
int stonith__execution_status(stonith_callback_data_t *data)
Definition: st_client.c:2387
bool stonith__event_state_eq(stonith_history_t *history, void *user_data)
Definition: st_client.c:2308
#define F_SUBTYPE
Definition: msg_xml.h:65
int stonith_api_kick(uint32_t nodeid, const char *uname, int timeout, bool off)
Definition: st_client.c:1905
#define F_STONITH_CALLBACK_TOKEN
Definition: internal.h:128
int stonith__lha_validate(stonith_t *st, int call_options, const char *target, const char *agent, GHashTable *params, int timeout, char **output, char **error_output)
Definition: st_lha.c:272
#define F_STONITH_CLIENTNAME
Definition: internal.h:129
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
Definition: nvpair.c:431
xmlNode * stonith_create_op(int call_id, const char *token, const char *op, xmlNode *data, int call_options)
Definition: st_client.c:784
int pcmk__substitute_secrets(const char *rsc_id, GHashTable *params)
Definition: cib_secrets.c:96
#define XML_TAG_FENCING_LEVEL
Definition: msg_xml.h:440
bool pcmk_stonith_param(const char *param)
Check whether a given stonith parameter is handled by Pacemaker.
Definition: agents.c:170
#define STONITH_OP_FENCE
Definition: internal.h:163
const char * stonith_action_str(const char *action)
Turn stonith action into a more readable string.
Definition: st_client.c:2042
struct stonith_key_value_s * next
Definition: stonith-ng.h:101
int stonith__result2rc(const pcmk__action_result_t *result)
Definition: st_actions.c:305
#define XML_TAG_ATTRS
Definition: msg_xml.h:211
enum pcmk_exec_status execution_status
struct mainloop_io_s mainloop_io_t
Definition: mainloop.h:33
Unspecified error.
Definition: results.h:240
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:323
gboolean stonith__later_succeeded(stonith_history_t *event, stonith_history_t *top_history)
Definition: st_client.c:2190
int(* register_level)(stonith_t *st, int options, const char *node, int level, stonith_key_value_t *device_list)
Register a fencing level for specified node with local fencer.
Definition: stonith-ng.h:205
void stonith_key_value_freeall(stonith_key_value_t *head, int keys, int values)
Definition: st_client.c:1884
#define F_STONITH_TIMEOUT
Definition: internal.h:110
int stonith__lha_metadata(const char *agent, int timeout, char **output)
Definition: st_lha.c:170
#define T_STONITH_TIMEOUT_VALUE
Definition: internal.h:155
const char * stonith__event_exit_reason(stonith_event_t *event)
Definition: st_client.c:2461
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:210
stonith_namespace
Definition: stonith-ng.h:81
long crm_ipc_read(crm_ipc_t *client)
Definition: ipc_client.c:1066
enum crm_ais_msg_types type
Definition: cpg.c:48
int call_id
Definition: internal.h:104
#define F_STONITH_NOTIFY_DEACTIVATE
Definition: internal.h:132
Action did not complete in time.
Definition: results.h:310
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:370
int crm_element_value_ll(const xmlNode *data, const char *name, long long *dest)
Retrieve the long long integer value of an XML attribute.
Definition: nvpair.c:597
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition: nvpair.c:565
int(* remove_callback)(stonith_t *st, int call_id, bool all_callbacks)
Remove a registered callback for a given call id.
Definition: stonith-ng.h:340
#define LOG_NEVER
Definition: logging.h:47
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
Definition: xpath.c:214
Wrappers for and extensions to glib mainloop.
#define STONITH_OP_LEVEL_DEL
Definition: internal.h:169
char * strndup(const char *str, size_t len)
#define STONITH_OP_DEVICE_ADD
Definition: internal.h:165
#define STONITH_OP_EXEC
Definition: internal.h:160
#define CRM_OP_REGISTER
Definition: crm.h:146
const char * action
Definition: pcmk_fence.c:29
xmlNode * string2xml(const char *input)
Definition: xml.c:869
const char * crm_ipc_buffer(crm_ipc_t *client)
Definition: ipc_client.c:1115
const char * stonith_namespace2text(enum stonith_namespace st_namespace)
Get agent namespace name.
Definition: st_client.c:130
int stonith__list_lha_agents(stonith_key_value_t **devices)
Definition: st_lha.c:110
#define F_STONITH_ACTION
Definition: internal.h:147
#define T_STONITH_NG
Definition: internal.h:150
gboolean stonith__watchdog_fencing_enabled_for_node(const char *node)
Definition: st_client.c:231
time_t stonith_api_time(uint32_t nodeid, const char *uname, bool in_progress)
Definition: st_client.c:1947
int(* free)(stonith_t *st)
Destroy the stonith api structure.
Definition: stonith-ng.h:155
int timeout
Definition: internal.h:105
#define PCMK__UNKNOWN_RESULT
#define XML_ATTR_STONITH_TARGET_PATTERN
Definition: msg_xml.h:444
xmlNode * create_level_registration_xml(const char *node, const char *pattern, const char *attr, const char *value, int level, stonith_key_value_t *device_list)
Definition: st_client.c:413
#define crm_warn(fmt, args...)
Definition: logging.h:359
const char * stonith_op_state_str(enum op_state state)
Return string equivalent of an operation state value.
Definition: st_client.c:2275
int stonith_api_connect_retry(stonith_t *st, const char *name, int max_attempts)
Make a blocking connection attempt to the fencer.
Definition: st_client.c:1840
const char * crm_exit_str(crm_exit_t exit_code)
Definition: results.c:506
void stonith__xe_get_result(xmlNode *xml, pcmk__action_result_t *result)
Definition: st_actions.c:469
stonith_t * st
Definition: pcmk_fence.c:27
bool stonith_dispatch(stonith_t *st)
Definition: st_client.c:1618
#define F_STONITH_CALLID
Definition: internal.h:101
#define crm_debug(fmt, args...)
Definition: logging.h:363
stonith_history_t * stonith__first_matching_event(stonith_history_t *history, bool(*matching_fn)(stonith_history_t *, void *), void *user_data)
Definition: st_client.c:2288
#define STONITH_OP_LEVEL_ADD
Definition: internal.h:168
struct crm_ipc_s crm_ipc_t
Definition: ipc.h:163
enum stonith_state state
Definition: stonith-ng.h:431
unsigned int tolerance
Definition: pcmk_fence.c:32
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:726
Used only to initialize variables.
Definition: results.h:306
#define XML_ATTR_ID
Definition: msg_xml.h:135
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:529
#define STONITH_OP_QUERY
Definition: internal.h:162
op_state
Definition: stonith-ng.h:71
int(* metadata)(stonith_t *st, int options, const char *device, const char *provider, char **output, int timeout)
Get the metadata documentation for a resource.
Definition: stonith-ng.h:215
#define F_STONITH_DEVICE
Definition: internal.h:146
#define XML_ATTR_STONITH_DEVICES
Definition: msg_xml.h:446
int(* register_device)(stonith_t *st, int options, const char *id, const char *provider, const char *agent, stonith_key_value_t *params)
Register a fence device with the local fencer.
Definition: stonith-ng.h:186
GList * stonith__parse_targets(const char *target_spec)
Definition: st_client.c:2150
int(* status)(stonith_t *st, int options, const char *id, const char *port, int timeout)
Check to see if a local stonith device&#39;s port is reachable.
Definition: stonith-ng.h:252
G_GNUC_INTERNAL int stonith__rhcs_validate(stonith_t *st, int call_options, const char *target, const char *agent, GHashTable *params, const char *host_arg, int timeout, char **output, char **error_output)
Definition: st_rhcs.c:256
enum stonith_namespace stonith_text2namespace(const char *namespace_s)
Get agent namespace by name.
Definition: st_client.c:104
guint ref
Definition: internal.h:106
int stonith__event_exit_status(stonith_event_t *event)
Definition: st_client.c:2421
#define crm_trace(fmt, args...)
Definition: logging.h:364
#define XML_ATTR_STONITH_INDEX
Definition: msg_xml.h:441
int(* fence)(stonith_t *st, int options, const char *node, const char *action, int timeout, int tolerance)
Issue a fencing action against a node.
Definition: stonith-ng.h:280
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int(* register_callback)(stonith_t *st, int call_id, int timeout, int options, void *userdata, const char *callback_name, void(*callback)(stonith_t *st, stonith_callback_data_t *data))
Register a callback to receive the result of an asynchronous call.
Definition: stonith-ng.h:327
#define stonith__set_call_options(st_call_opts, call_for, flags_to_set)
Definition: internal.h:35
#define crm_log_xml_debug(xml, text)
Definition: logging.h:371
bool stonith_agent_exists(const char *agent, int timeout)
Definition: st_client.c:2011
#define XML_ATTR_STONITH_TARGET
Definition: msg_xml.h:442
void pcmk__str_update(char **str, const char *value)
Definition: strings.c:1188
gboolean pcmk__str_in_list(const gchar *s, GList *lst, uint32_t flags)
Definition: strings.c:886
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:696
#define F_STONITH_STATE
Definition: internal.h:142
#define crm_log_xml_warn(xml, text)
Definition: logging.h:368
char * stonith__event_description(stonith_event_t *event)
Definition: st_client.c:2485
Action completed, result is known.
Definition: results.h:308
int(* disconnect)(stonith_t *st)
Disconnect from the local stonith daemon.
Definition: stonith-ng.h:169
int(* stonith_op_t)(const char *, int, const char *, xmlNode *, xmlNode *, xmlNode *, xmlNode **, xmlNode **)
Definition: st_client.c:82
enum stonith_namespace stonith_get_namespace(const char *agent, const char *namespace_s)
Determine namespace of a fence agent.
Definition: st_client.c:151
#define T_STONITH_NOTIFY_DISCONNECT
Definition: stonith-ng.h:35
const char * get_stonith_provider(const char *agent, const char *provider)
Definition: st_client.c:2523
#define STONITH_OP_DEVICE_DEL
Definition: internal.h:166
int(* list)(stonith_t *st, int options, const char *id, char **list_output, int timeout)
Retrieve string listing hosts and port assignments from a local stonith device.
Definition: stonith-ng.h:236
#define ECOMM
Definition: portability.h:125
void mainloop_del_ipc_client(mainloop_io_t *client)
Definition: mainloop.c:961
Success.
Definition: results.h:239
void crm_ipc_destroy(crm_ipc_t *client)
Definition: ipc_client.c:930
#define XML_ATTR_STONITH_TARGET_VALUE
Definition: msg_xml.h:443
G_GNUC_INTERNAL bool stonith__agent_is_rhcs(const char *agent)
Definition: st_rhcs.c:245
#define F_STONITH_TARGET
Definition: internal.h:104
struct stonith_notify_client_s stonith_notify_client_t
#define XML_LRM_ATTR_EXIT_REASON
Definition: msg_xml.h:321
int(* list_agents)(stonith_t *stonith, int call_options, const char *provider, stonith_key_value_t **devices, int timeout)
Retrieve a list of installed stonith agents.
Definition: stonith-ng.h:227
gboolean add_message_xml(xmlNode *msg, const char *field, xmlNode *xml)
Definition: messages.c:162
void stonith_api_delete(stonith_t *stonith)
Definition: st_client.c:1676
int(* fence_with_delay)(stonith_t *st, int options, const char *node, const char *action, int timeout, int tolerance, int delay)
Issue a fencing action against a node with requested fencing delay.
Definition: stonith-ng.h:424
void free_xml(xmlNode *child)
Definition: xml.c:824
int(* connect)(stonith_t *st, const char *name, int *stonith_fd)
Connect to the local stonith daemon.
Definition: stonith-ng.h:162
#define api_log(level, fmt, args...)
Definition: st_client.c:1902
const char * stonith__exit_reason(stonith_callback_data_t *data)
Definition: st_client.c:2404
uint32_t id
Definition: cpg.c:45
#define F_STONITH_OUTPUT
Definition: internal.h:108
#define STONITH_OP_FENCE_HISTORY
Definition: internal.h:167
#define F_STONITH_CALLOPTS
Definition: internal.h:100
stonith_history_t * stonith__sort_history(stonith_history_t *history)
Definition: st_client.c:2221
int(* remove_device)(stonith_t *st, int options, const char *name)
Unregister a fence device with the local fencer.
Definition: stonith-ng.h:177
struct stonith_private_s stonith_private_t
bool crm_ipc_connected(crm_ipc_t *client)
Definition: ipc_client.c:970
const char * target
Definition: pcmk_fence.c:28
#define CRM_XS
Definition: logging.h:55
int(* register_notification)(stonith_t *st, const char *event, void(*notify)(stonith_t *st, stonith_event_t *e))
Definition: stonith-ng.h:298
gboolean stonith__watchdog_fencing_enabled_for_node_api(stonith_t *st, const char *node)
Definition: st_client.c:172
int crm_ipc_ready(crm_ipc_t *client)
Check whether an IPC connection is ready to be read.
Definition: ipc_client.c:1002
int(* monitor)(stonith_t *st, int options, const char *id, int timeout)
Check to see if a local stonith device is reachable.
Definition: stonith-ng.h:244
char * client_origin
Definition: stonith-ng.h:135
#define F_STONITH_DATE
Definition: internal.h:140
#define PCMK_STONITH_HOST_ARGUMENT
Definition: agents.h:32
int(* remove_notification)(stonith_t *st, const char *event)
Remove a previously registered notification for event, or all notifications if NULL.
Definition: stonith-ng.h:311
G_GNUC_INTERNAL int stonith__list_rhcs_agents(stonith_key_value_t **devices)
Definition: st_rhcs.c:35
#define crm_log_xml_err(xml, text)
Definition: logging.h:367
struct stonith_callback_client_s stonith_callback_client_t
int(* validate)(stonith_t *st, int call_options, const char *rsc_id, const char *namespace_s, const char *agent, stonith_key_value_t *params, int timeout, char **output, char **error_output)
Validate an arbitrary stonith device configuration.
Definition: stonith-ng.h:402
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:611
pcmk__action_result_t result
Definition: pcmk_fence.c:34
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:309
int stonith__event_execution_status(stonith_event_t *event)
Definition: st_client.c:2441
#define pcmk__set_ipc_flags(ipc_flags, ipc_name, flags_to_set)
Definition: ipc_internal.h:176
crm_ipc_t * mainloop_get_ipc_client(mainloop_io_t *client)
Definition: mainloop.c:967
#define F_STONITH_DELAY
Definition: internal.h:112
#define crm_err(fmt, args...)
Definition: logging.h:358
#define G_PRIORITY_MEDIUM
Definition: mainloop.h:182
#define CRM_ASSERT(expr)
Definition: results.h:42
xmlXPathObjectPtr xpath_search(xmlNode *xml_top, const char *path)
Definition: xpath.c:139
#define ENOTUNIQ
Definition: portability.h:120
int(* remove_level_full)(stonith_t *st, int options, const char *node, const char *pattern, const char *attr, const char *value, int level)
Unregister fencing level for specified node, pattern or attribute.
Definition: stonith-ng.h:358
stonith_api_operations_t * cmds
Definition: stonith-ng.h:437
int crm_ipc_send(crm_ipc_t *client, xmlNode *message, enum crm_ipc_flags flags, int32_t ms_timeout, xmlNode **reply)
Send an IPC XML message.
Definition: ipc_client.c:1209
stonith_t * stonith_api_new(void)
Definition: st_client.c:1762
int(* remove_level)(stonith_t *st, int options, const char *node, int level)
Unregister a fencing level for specified node with local fencer.
Definition: stonith-ng.h:196
int delay
Definition: pcmk_fence.c:33
#define crm_log_xml_notice(xml, text)
Definition: logging.h:369
Fencing aka. STONITH.
xmlNode * getXpathResult(xmlXPathObjectPtr xpathObj, int index)
Definition: xpath.c:58
char * executioner
Definition: stonith-ng.h:130
crm_ipc_t * crm_ipc_new(const char *name, size_t max_size)
Create a new (legacy) object for using Pacemaker daemon IPC.
Definition: ipc_client.c:800
char * operation
Definition: stonith-ng.h:124
char uname[MAX_NAME]
Definition: cpg.c:50
#define crm_str(x)
Definition: logging.h:384
G_GNUC_INTERNAL int stonith__rhcs_metadata(const char *agent, int timeout, char **output)
Execute RHCS-compatible agent&#39;s meta-data action.
Definition: st_rhcs.c:219
#define F_STONITH_ORIGIN
Definition: internal.h:138
#define pcmk_ok
Definition: results.h:68
bool stonith__agent_is_lha(const char *agent)
Determine namespace of a fence agent.
Definition: st_lha.c:83
bool stonith__event_state_pending(stonith_history_t *history, void *user_data)
Definition: st_client.c:2302
#define T_STONITH_NOTIFY_FENCE
Definition: stonith-ng.h:36
int(* confirm)(stonith_t *st, int options, const char *node)
Manually confirm that a node is down.
Definition: stonith-ng.h:289
void * st_private
Definition: stonith-ng.h:435
#define T_STONITH_NOTIFY
Definition: internal.h:156
void pcmk__set_result(pcmk__action_result_t *result, int exit_status, enum pcmk_exec_status exec_status, const char *exit_reason)
Definition: results.c:814
#define crm_log_xml_trace(xml, text)
Definition: logging.h:372
#define F_STONITH_OPERATION
Definition: internal.h:103
int stonith__exit_status(stonith_callback_data_t *data)
Definition: st_client.c:2370
mainloop_io_t * mainloop_add_ipc_client(const char *name, int priority, size_t max_size, void *userdata, struct ipc_client_callbacks *callbacks)
Definition: mainloop.c:936
#define F_STONITH_NOTIFY_ACTIVATE
Definition: internal.h:131
#define F_XML_TAGNAME
Definition: msg_xml.h:77
#define STONITH_WATCHDOG_ID
Definition: internal.h:174
void pcmk__reset_result(pcmk__action_result_t *result)
Definition: results.c:903
void freeXpathObject(xmlXPathObjectPtr xpathObj)
Definition: xpath.c:39
crm_ipc_flags
Definition: ipc.h:144
void crm_ipc_close(crm_ipc_t *client)
Definition: ipc_client.c:917
#define F_STONITH_CALLDATA
Definition: internal.h:102
CRM_TRACE_INIT_DATA(stonith)
unsigned int timeout
Definition: pcmk_fence.c:31
Execution failed, may be retried.
Definition: results.h:312
#define F_STONITH_DELEGATE
Definition: internal.h:133
#define crm_info(fmt, args...)
Definition: logging.h:361
int call_id
Definition: stonith-ng.h:433
int(* history)(stonith_t *st, int options, const char *node, stonith_history_t **output, int timeout)
Retrieve a list of fencing operations that have occurred for a specific node.
Definition: stonith-ng.h:296
int(* dispatch)(const char *buffer, ssize_t length, gpointer userdata)
Dispatch function for an IPC connection used as mainloop source.
Definition: mainloop.h:84
#define F_STONITH_HISTORY_LIST
Definition: internal.h:139
#define F_STONITH_TOLERANCE
Definition: internal.h:111