pacemaker  2.1.3-ea053b43a
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;
709 
710  kvp = calloc(1, sizeof(stonith_history_t));
716  crm_element_value_ll(op, F_STONITH_DATE, &completed);
717  kvp->completed = (time_t) completed;
718  crm_element_value_ll(op, F_STONITH_DATE_NSEC, &completed_nsec);
719  kvp->completed_nsec = completed_nsec;
721 
724  result.exit_reason = NULL;
726 
727  if (last) {
728  last->next = kvp;
729  } else {
730  *history = kvp;
731  }
732  last = kvp;
733  }
734  }
735 
736  free_xml(output);
737 
738  return rc;
739 }
740 
742 {
743  stonith_history_t *hp, *hp_old;
744 
745  for (hp = history; hp; hp_old = hp, hp = hp->next, free(hp_old)) {
746  free(hp->target);
747  free(hp->action);
748  free(hp->origin);
749  free(hp->delegate);
750  free(hp->client);
751  free(hp->exit_reason);
752  }
753 }
754 
755 static gint
756 stonithlib_GCompareFunc(gconstpointer a, gconstpointer b)
757 {
758  int rc = 0;
759  const stonith_notify_client_t *a_client = a;
760  const stonith_notify_client_t *b_client = b;
761 
762  if (a_client->delete || b_client->delete) {
763  /* make entries marked for deletion not findable */
764  return -1;
765  }
766  CRM_CHECK(a_client->event != NULL && b_client->event != NULL, return 0);
767  rc = strcmp(a_client->event, b_client->event);
768  if (rc == 0) {
769  if (a_client->notify == NULL || b_client->notify == NULL) {
770  return 0;
771 
772  } else if (a_client->notify == b_client->notify) {
773  return 0;
774 
775  } else if (((long)a_client->notify) < ((long)b_client->notify)) {
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  crm_err("callbacks for %s are not equal: %p vs. %p",
781  a_client->event, a_client->notify, b_client->notify);
782  return 1;
783  }
784  return rc;
785 }
786 
787 xmlNode *
788 stonith_create_op(int call_id, const char *token, const char *op, xmlNode * data, int call_options)
789 {
790  xmlNode *op_msg = create_xml_node(NULL, "stonith_command");
791 
792  CRM_CHECK(op_msg != NULL, return NULL);
793  CRM_CHECK(token != NULL, return NULL);
794 
795  crm_xml_add(op_msg, F_XML_TAGNAME, "stonith_command");
796 
797  crm_xml_add(op_msg, F_TYPE, T_STONITH_NG);
798  crm_xml_add(op_msg, F_STONITH_CALLBACK_TOKEN, token);
799  crm_xml_add(op_msg, F_STONITH_OPERATION, op);
800  crm_xml_add_int(op_msg, F_STONITH_CALLID, call_id);
801  crm_trace("Sending call options: %.8lx, %d", (long)call_options, call_options);
802  crm_xml_add_int(op_msg, F_STONITH_CALLOPTS, call_options);
803 
804  if (data != NULL) {
806  }
807 
808  return op_msg;
809 }
810 
811 static void
812 stonith_destroy_op_callback(gpointer data)
813 {
815 
816  if (blob->timer && blob->timer->ref > 0) {
817  g_source_remove(blob->timer->ref);
818  }
819  free(blob->timer);
820  free(blob);
821 }
822 
823 static int
824 stonith_api_signoff(stonith_t * stonith)
825 {
826  stonith_private_t *native = stonith->st_private;
827 
828  crm_debug("Disconnecting from the fencer");
829 
830  if (native->source != NULL) {
831  /* Attached to mainloop */
832  mainloop_del_ipc_client(native->source);
833  native->source = NULL;
834  native->ipc = NULL;
835 
836  } else if (native->ipc) {
837  /* Not attached to mainloop */
838  crm_ipc_t *ipc = native->ipc;
839 
840  native->ipc = NULL;
841  crm_ipc_close(ipc);
842  crm_ipc_destroy(ipc);
843  }
844 
845  free(native->token); native->token = NULL;
846  stonith->state = stonith_disconnected;
847  return pcmk_ok;
848 }
849 
850 static int
851 stonith_api_del_callback(stonith_t * stonith, int call_id, bool all_callbacks)
852 {
853  stonith_private_t *private = stonith->st_private;
854 
855  if (all_callbacks) {
856  private->op_callback = NULL;
857  g_hash_table_destroy(private->stonith_op_callback_table);
858  private->stonith_op_callback_table = pcmk__intkey_table(stonith_destroy_op_callback);
859 
860  } else if (call_id == 0) {
861  private->op_callback = NULL;
862 
863  } else {
864  pcmk__intkey_table_remove(private->stonith_op_callback_table, call_id);
865  }
866  return pcmk_ok;
867 }
868 
880 static void
881 invoke_fence_action_callback(stonith_t *st, int call_id,
883  void *userdata,
884  void (*callback) (stonith_t *st,
886 {
887  stonith_callback_data_t data = { 0, };
888 
889  data.call_id = call_id;
891  data.userdata = userdata;
892  data.opaque = (void *) result;
893 
894  callback(st, &data);
895 }
896 
908 static void
909 invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id)
910 {
911  stonith_private_t *private = NULL;
912  stonith_callback_client_t *cb_info = NULL;
914 
915  CRM_CHECK(stonith != NULL, return);
916  CRM_CHECK(stonith->st_private != NULL, return);
917 
918  private = stonith->st_private;
919 
920  if (msg == NULL) {
921  // Fencer didn't reply in time
923  "Fencer accepted request but did not reply in time");
924  CRM_LOG_ASSERT(call_id > 0);
925 
926  } else {
927  // We have the fencer reply
928  if ((crm_element_value_int(msg, F_STONITH_CALLID, &call_id) != 0)
929  || (call_id <= 0)) {
930  crm_log_xml_warn(msg, "Bad fencer reply");
931  }
933  }
934 
935  if (call_id > 0) {
936  cb_info = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
937  call_id);
938  }
939 
940  if ((cb_info != NULL) && (cb_info->callback != NULL)
941  && (pcmk__result_ok(&result) || !(cb_info->only_success))) {
942  crm_trace("Invoking callback %s for call %d",
943  crm_str(cb_info->id), call_id);
944  invoke_fence_action_callback(stonith, call_id, &result,
945  cb_info->user_data, cb_info->callback);
946 
947  } else if ((private->op_callback == NULL) && !pcmk__result_ok(&result)) {
948  crm_warn("Fencing action without registered callback failed: %d (%s%s%s)",
950  pcmk_exec_status_str(result.execution_status),
951  ((result.exit_reason == NULL)? "" : ": "),
952  ((result.exit_reason == NULL)? "" : result.exit_reason));
953  crm_log_xml_debug(msg, "Failed fence update");
954  }
955 
956  if (private->op_callback != NULL) {
957  crm_trace("Invoking global callback for call %d", call_id);
958  invoke_fence_action_callback(stonith, call_id, &result, NULL,
959  private->op_callback);
960  }
961 
962  if (cb_info != NULL) {
963  stonith_api_del_callback(stonith, call_id, FALSE);
964  }
966 }
967 
968 static gboolean
969 stonith_async_timeout_handler(gpointer data)
970 {
971  struct timer_rec_s *timer = data;
972 
973  crm_err("Async call %d timed out after %dms", timer->call_id, timer->timeout);
974  invoke_registered_callbacks(timer->stonith, NULL, timer->call_id);
975 
976  /* Always return TRUE, never remove the handler
977  * We do that in stonith_del_callback()
978  */
979  return TRUE;
980 }
981 
982 static void
983 set_callback_timeout(stonith_callback_client_t * callback, stonith_t * stonith, int call_id,
984  int timeout)
985 {
986  struct timer_rec_s *async_timer = callback->timer;
987 
988  if (timeout <= 0) {
989  return;
990  }
991 
992  if (!async_timer) {
993  async_timer = calloc(1, sizeof(struct timer_rec_s));
994  callback->timer = async_timer;
995  }
996 
997  async_timer->stonith = stonith;
998  async_timer->call_id = call_id;
999  /* Allow a fair bit of grace to allow the server to tell us of a timeout
1000  * This is only a fallback
1001  */
1002  async_timer->timeout = (timeout + 60) * 1000;
1003  if (async_timer->ref) {
1004  g_source_remove(async_timer->ref);
1005  }
1006  async_timer->ref =
1007  g_timeout_add(async_timer->timeout, stonith_async_timeout_handler, async_timer);
1008 }
1009 
1010 static void
1011 update_callback_timeout(int call_id, int timeout, stonith_t * st)
1012 {
1013  stonith_callback_client_t *callback = NULL;
1014  stonith_private_t *private = st->st_private;
1015 
1016  callback = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
1017  call_id);
1018  if (!callback || !callback->allow_timeout_updates) {
1019  return;
1020  }
1021 
1022  set_callback_timeout(callback, st, call_id, timeout);
1023 }
1024 
1025 static int
1026 stonith_dispatch_internal(const char *buffer, ssize_t length, gpointer userdata)
1027 {
1028  const char *type = NULL;
1029  struct notify_blob_s blob;
1030 
1031  stonith_t *st = userdata;
1032  stonith_private_t *private = NULL;
1033 
1034  CRM_ASSERT(st != NULL);
1035  private = st->st_private;
1036 
1037  blob.stonith = st;
1038  blob.xml = string2xml(buffer);
1039  if (blob.xml == NULL) {
1040  crm_warn("Received malformed message from fencer: %s", buffer);
1041  return 0;
1042  }
1043 
1044  /* do callbacks */
1045  type = crm_element_value(blob.xml, F_TYPE);
1046  crm_trace("Activating %s callbacks...", type);
1047 
1048  if (pcmk__str_eq(type, T_STONITH_NG, pcmk__str_casei)) {
1049  invoke_registered_callbacks(st, blob.xml, 0);
1050 
1051  } else if (pcmk__str_eq(type, T_STONITH_NOTIFY, pcmk__str_casei)) {
1052  foreach_notify_entry(private, stonith_send_notification, &blob);
1053  } else if (pcmk__str_eq(type, T_STONITH_TIMEOUT_VALUE, pcmk__str_casei)) {
1054  int call_id = 0;
1055  int timeout = 0;
1056 
1058  crm_element_value_int(blob.xml, F_STONITH_CALLID, &call_id);
1059 
1060  update_callback_timeout(call_id, timeout, st);
1061  } else {
1062  crm_err("Unknown message type: %s", type);
1063  crm_log_xml_warn(blob.xml, "BadReply");
1064  }
1065 
1066  free_xml(blob.xml);
1067  return 1;
1068 }
1069 
1070 static int
1071 stonith_api_signon(stonith_t * stonith, const char *name, int *stonith_fd)
1072 {
1073  int rc = pcmk_ok;
1074  stonith_private_t *native = NULL;
1075  const char *display_name = name? name : "client";
1076 
1077  struct ipc_client_callbacks st_callbacks = {
1078  .dispatch = stonith_dispatch_internal,
1079  .destroy = stonith_connection_destroy
1080  };
1081 
1082  CRM_CHECK(stonith != NULL, return -EINVAL);
1083 
1084  native = stonith->st_private;
1085  CRM_ASSERT(native != NULL);
1086 
1087  crm_debug("Attempting fencer connection by %s with%s mainloop",
1088  display_name, (stonith_fd? "out" : ""));
1089 
1090  stonith->state = stonith_connected_command;
1091  if (stonith_fd) {
1092  /* No mainloop */
1093  native->ipc = crm_ipc_new("stonith-ng", 0);
1094 
1095  if (native->ipc && crm_ipc_connect(native->ipc)) {
1096  *stonith_fd = crm_ipc_get_fd(native->ipc);
1097  } else if (native->ipc) {
1098  crm_ipc_close(native->ipc);
1099  crm_ipc_destroy(native->ipc);
1100  native->ipc = NULL;
1101  }
1102 
1103  } else {
1104  /* With mainloop */
1105  native->source =
1106  mainloop_add_ipc_client("stonith-ng", G_PRIORITY_MEDIUM, 0, stonith, &st_callbacks);
1107  native->ipc = mainloop_get_ipc_client(native->source);
1108  }
1109 
1110  if (native->ipc == NULL) {
1111  rc = -ENOTCONN;
1112  } else {
1113  xmlNode *reply = NULL;
1114  xmlNode *hello = create_xml_node(NULL, "stonith_command");
1115 
1116  crm_xml_add(hello, F_TYPE, T_STONITH_NG);
1119  rc = crm_ipc_send(native->ipc, hello, crm_ipc_client_response, -1, &reply);
1120 
1121  if (rc < 0) {
1122  crm_debug("Couldn't register with the fencer: %s "
1123  CRM_XS " rc=%d", pcmk_strerror(rc), rc);
1124  rc = -ECOMM;
1125 
1126  } else if (reply == NULL) {
1127  crm_debug("Couldn't register with the fencer: no reply");
1128  rc = -EPROTO;
1129 
1130  } else {
1131  const char *msg_type = crm_element_value(reply, F_STONITH_OPERATION);
1132 
1133  native->token = crm_element_value_copy(reply, F_STONITH_CLIENTID);
1134  if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_casei)) {
1135  crm_debug("Couldn't register with the fencer: invalid reply type '%s'",
1136  (msg_type? msg_type : "(missing)"));
1137  crm_log_xml_debug(reply, "Invalid fencer reply");
1138  rc = -EPROTO;
1139 
1140  } else if (native->token == NULL) {
1141  crm_debug("Couldn't register with the fencer: no token in reply");
1142  crm_log_xml_debug(reply, "Invalid fencer reply");
1143  rc = -EPROTO;
1144 
1145  } else {
1146  crm_debug("Connection to fencer by %s succeeded (registration token: %s)",
1147  display_name, native->token);
1148  rc = pcmk_ok;
1149  }
1150  }
1151 
1152  free_xml(reply);
1153  free_xml(hello);
1154  }
1155 
1156  if (rc != pcmk_ok) {
1157  crm_debug("Connection attempt to fencer by %s failed: %s "
1158  CRM_XS " rc=%d", display_name, pcmk_strerror(rc), rc);
1159  stonith->cmds->disconnect(stonith);
1160  }
1161  return rc;
1162 }
1163 
1164 static int
1165 stonith_set_notification(stonith_t * stonith, const char *callback, int enabled)
1166 {
1167  int rc = pcmk_ok;
1168  xmlNode *notify_msg = create_xml_node(NULL, __func__);
1169  stonith_private_t *native = stonith->st_private;
1170 
1171  if (stonith->state != stonith_disconnected) {
1172 
1174  if (enabled) {
1175  crm_xml_add(notify_msg, F_STONITH_NOTIFY_ACTIVATE, callback);
1176  } else {
1177  crm_xml_add(notify_msg, F_STONITH_NOTIFY_DEACTIVATE, callback);
1178  }
1179 
1180  rc = crm_ipc_send(native->ipc, notify_msg, crm_ipc_client_response, -1, NULL);
1181  if (rc < 0) {
1182  crm_perror(LOG_DEBUG, "Couldn't register for fencing notifications: %d", rc);
1183  rc = -ECOMM;
1184  } else {
1185  rc = pcmk_ok;
1186  }
1187  }
1188 
1189  free_xml(notify_msg);
1190  return rc;
1191 }
1192 
1193 static int
1194 stonith_api_add_notification(stonith_t * stonith, const char *event,
1195  void (*callback) (stonith_t * stonith, stonith_event_t * e))
1196 {
1197  GList *list_item = NULL;
1198  stonith_notify_client_t *new_client = NULL;
1199  stonith_private_t *private = NULL;
1200 
1201  private = stonith->st_private;
1202  crm_trace("Adding callback for %s events (%d)", event, g_list_length(private->notify_list));
1203 
1204  new_client = calloc(1, sizeof(stonith_notify_client_t));
1205  new_client->event = event;
1206  new_client->notify = callback;
1207 
1208  list_item = g_list_find_custom(private->notify_list, new_client, stonithlib_GCompareFunc);
1209 
1210  if (list_item != NULL) {
1211  crm_warn("Callback already present");
1212  free(new_client);
1213  return -ENOTUNIQ;
1214 
1215  } else {
1216  private->notify_list = g_list_append(private->notify_list, new_client);
1217 
1218  stonith_set_notification(stonith, event, 1);
1219 
1220  crm_trace("Callback added (%d)", g_list_length(private->notify_list));
1221  }
1222  return pcmk_ok;
1223 }
1224 
1225 static void
1226 del_notify_entry(gpointer data, gpointer user_data)
1227 {
1228  stonith_notify_client_t *entry = data;
1229  stonith_t * stonith = user_data;
1230 
1231  if (!entry->delete) {
1232  crm_debug("Removing callback for %s events", entry->event);
1233  stonith_api_del_notification(stonith, entry->event);
1234  }
1235 }
1236 
1237 static int
1238 stonith_api_del_notification(stonith_t * stonith, const char *event)
1239 {
1240  GList *list_item = NULL;
1241  stonith_notify_client_t *new_client = NULL;
1242  stonith_private_t *private = stonith->st_private;
1243 
1244  if (event == NULL) {
1245  foreach_notify_entry(private, del_notify_entry, stonith);
1246  crm_trace("Removed callback");
1247 
1248  return pcmk_ok;
1249  }
1250 
1251  crm_debug("Removing callback for %s events", event);
1252 
1253  new_client = calloc(1, sizeof(stonith_notify_client_t));
1254  new_client->event = event;
1255  new_client->notify = NULL;
1256 
1257  list_item = g_list_find_custom(private->notify_list, new_client, stonithlib_GCompareFunc);
1258 
1259  stonith_set_notification(stonith, event, 0);
1260 
1261  if (list_item != NULL) {
1262  stonith_notify_client_t *list_client = list_item->data;
1263 
1264  if (private->notify_refcnt) {
1265  list_client->delete = TRUE;
1266  private->notify_deletes = TRUE;
1267  } else {
1268  private->notify_list = g_list_remove(private->notify_list, list_client);
1269  free(list_client);
1270  }
1271 
1272  crm_trace("Removed callback");
1273 
1274  } else {
1275  crm_trace("Callback not present");
1276  }
1277  free(new_client);
1278  return pcmk_ok;
1279 }
1280 
1281 static int
1282 stonith_api_add_callback(stonith_t * stonith, int call_id, int timeout, int options,
1283  void *user_data, const char *callback_name,
1284  void (*callback) (stonith_t * st, stonith_callback_data_t * data))
1285 {
1286  stonith_callback_client_t *blob = NULL;
1287  stonith_private_t *private = NULL;
1288 
1289  CRM_CHECK(stonith != NULL, return -EINVAL);
1290  CRM_CHECK(stonith->st_private != NULL, return -EINVAL);
1291  private = stonith->st_private;
1292 
1293  if (call_id == 0) { // Add global callback
1294  private->op_callback = callback;
1295 
1296  } else if (call_id < 0) { // Call failed immediately, so call callback now
1297  if (!(options & st_opt_report_only_success)) {
1299 
1300  crm_trace("Call failed, calling %s: %s", callback_name, pcmk_strerror(call_id));
1302  stonith__legacy2status(call_id), NULL);
1303  invoke_fence_action_callback(stonith, call_id, &result,
1304  user_data, callback);
1305  } else {
1306  crm_warn("Fencer call failed: %s", pcmk_strerror(call_id));
1307  }
1308  return FALSE;
1309  }
1310 
1311  blob = calloc(1, sizeof(stonith_callback_client_t));
1312  blob->id = callback_name;
1313  blob->only_success = (options & st_opt_report_only_success) ? TRUE : FALSE;
1314  blob->user_data = user_data;
1315  blob->callback = callback;
1316  blob->allow_timeout_updates = (options & st_opt_timeout_updates) ? TRUE : FALSE;
1317 
1318  if (timeout > 0) {
1319  set_callback_timeout(blob, stonith, call_id, timeout);
1320  }
1321 
1322  pcmk__intkey_table_insert(private->stonith_op_callback_table, call_id,
1323  blob);
1324  crm_trace("Added callback to %s for call %d", callback_name, call_id);
1325 
1326  return TRUE;
1327 }
1328 
1329 static void
1330 stonith_dump_pending_op(gpointer key, gpointer value, gpointer user_data)
1331 {
1332  int call = GPOINTER_TO_INT(key);
1333  stonith_callback_client_t *blob = value;
1334 
1335  crm_debug("Call %d (%s): pending", call, crm_str(blob->id));
1336 }
1337 
1338 void
1340 {
1341  stonith_private_t *private = stonith->st_private;
1342 
1343  if (private->stonith_op_callback_table == NULL) {
1344  return;
1345  }
1346  return g_hash_table_foreach(private->stonith_op_callback_table, stonith_dump_pending_op, NULL);
1347 }
1348 
1356 static xmlNode *
1357 get_event_data_xml(xmlNode *msg, const char *ntype)
1358 {
1359  char *data_addr = crm_strdup_printf("//%s", ntype);
1360  xmlNode *data = get_xpath_object(data_addr, msg, LOG_DEBUG);
1361 
1362  free(data_addr);
1363  return data;
1364 }
1365 
1366 /*
1367  <notify t="st_notify" subt="st_device_register" st_op="st_device_register" st_rc="0" >
1368  <st_calldata >
1369  <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" >
1370  <st_calldata >
1371  <st_device_id id="test-id" origin="create_device_registration_xml" agent="fence_virsh" namespace="stonith-ng" >
1372  <attributes ipaddr="localhost" pcmk-portmal="some-host=pcmk-1 pcmk-3=3,4" login="root" identity_file="/root/.ssh/id_dsa" />
1373  </st_device_id>
1374  </st_calldata>
1375  </stonith_command>
1376  </st_calldata>
1377  </notify>
1378 
1379  <notify t="st_notify" subt="st_notify_fence" st_op="st_notify_fence" st_rc="0" >
1380  <st_calldata >
1381  <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" />
1382  </st_calldata>
1383  </notify>
1384 */
1385 static stonith_event_t *
1386 xml_to_event(xmlNode *msg)
1387 {
1388  stonith_event_t *event = calloc(1, sizeof(stonith_event_t));
1389  const char *ntype = crm_element_value(msg, F_SUBTYPE);
1390  struct event_private *event_private = NULL;
1391 
1392  CRM_ASSERT(event != NULL);
1393 
1394  event->opaque = calloc(1, sizeof(struct event_private));
1395  CRM_ASSERT(event->opaque != NULL);
1396  event_private = (struct event_private *) event->opaque;
1397 
1398  crm_log_xml_trace(msg, "stonith_notify");
1399 
1400  // All notification types have the operation result
1401  stonith__xe_get_result(msg, &event_private->result);
1402 
1403  // @COMPAT The API originally provided the result as a legacy return code
1404  event->result = pcmk_rc2legacy(stonith__result2rc(&event_private->result));
1405 
1406  // Fence notifications have additional information
1407  if (pcmk__str_eq(ntype, T_STONITH_NOTIFY_FENCE, pcmk__str_casei)) {
1408  xmlNode *data = get_event_data_xml(msg, ntype);
1409 
1410  if (data == NULL) {
1411  crm_err("No data for %s event", ntype);
1412  crm_log_xml_notice(msg, "BadEvent");
1413  } else {
1414  event->origin = crm_element_value_copy(data, F_STONITH_ORIGIN);
1415  event->action = crm_element_value_copy(data, F_STONITH_ACTION);
1416  event->target = crm_element_value_copy(data, F_STONITH_TARGET);
1417  event->executioner = crm_element_value_copy(data, F_STONITH_DELEGATE);
1419  event->client_origin = crm_element_value_copy(data, F_STONITH_CLIENTNAME);
1420  event->device = crm_element_value_copy(data, F_STONITH_DEVICE);
1421  }
1422  event->operation = crm_element_value_copy(msg, F_STONITH_OPERATION);
1423  }
1424 
1425  return event;
1426 }
1427 
1428 static void
1429 event_free(stonith_event_t * event)
1430 {
1431  struct event_private *event_private = event->opaque;
1432 
1433  free(event->id);
1434  free(event->type);
1435  free(event->message);
1436  free(event->operation);
1437  free(event->origin);
1438  free(event->action);
1439  free(event->target);
1440  free(event->executioner);
1441  free(event->device);
1442  free(event->client_origin);
1443  pcmk__reset_result(&event_private->result);
1444  free(event->opaque);
1445  free(event);
1446 }
1447 
1448 static void
1449 stonith_send_notification(gpointer data, gpointer user_data)
1450 {
1451  struct notify_blob_s *blob = user_data;
1452  stonith_notify_client_t *entry = data;
1453  stonith_event_t *st_event = NULL;
1454  const char *event = NULL;
1455 
1456  if (blob->xml == NULL) {
1457  crm_warn("Skipping callback - NULL message");
1458  return;
1459  }
1460 
1461  event = crm_element_value(blob->xml, F_SUBTYPE);
1462 
1463  if (entry == NULL) {
1464  crm_warn("Skipping callback - NULL callback client");
1465  return;
1466 
1467  } else if (entry->delete) {
1468  crm_trace("Skipping callback - marked for deletion");
1469  return;
1470 
1471  } else if (entry->notify == NULL) {
1472  crm_warn("Skipping callback - NULL callback");
1473  return;
1474 
1475  } else if (!pcmk__str_eq(entry->event, event, pcmk__str_casei)) {
1476  crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event);
1477  return;
1478  }
1479 
1480  st_event = xml_to_event(blob->xml);
1481 
1482  crm_trace("Invoking callback for %p/%s event...", entry, event);
1483  entry->notify(blob->stonith, st_event);
1484  crm_trace("Callback invoked...");
1485 
1486  event_free(st_event);
1487 }
1488 
1503 static int
1504 stonith_send_command(stonith_t * stonith, const char *op, xmlNode * data, xmlNode ** output_data,
1505  int call_options, int timeout)
1506 {
1507  int rc = 0;
1508  int reply_id = -1;
1509 
1510  xmlNode *op_msg = NULL;
1511  xmlNode *op_reply = NULL;
1512  stonith_private_t *native = NULL;
1513 
1514  CRM_ASSERT(stonith && stonith->st_private && op);
1515  native = stonith->st_private;
1516 
1517  if (output_data != NULL) {
1518  *output_data = NULL;
1519  }
1520 
1521  if ((stonith->state == stonith_disconnected) || (native->token == NULL)) {
1522  return -ENOTCONN;
1523  }
1524 
1525  /* Increment the call ID, which must be positive to avoid conflicting with
1526  * error codes. This shouldn't be a problem unless the client mucked with
1527  * it or the counter wrapped around.
1528  */
1529  stonith->call_id++;
1530  if (stonith->call_id < 1) {
1531  stonith->call_id = 1;
1532  }
1533 
1534  op_msg = stonith_create_op(stonith->call_id, native->token, op, data, call_options);
1535  if (op_msg == NULL) {
1536  return -EINVAL;
1537  }
1538 
1540  crm_trace("Sending %s message to fencer with timeout %ds", op, timeout);
1541 
1542  if (data) {
1543  const char *delay_s = crm_element_value(data, F_STONITH_DELAY);
1544 
1545  if (delay_s) {
1546  crm_xml_add(op_msg, F_STONITH_DELAY, delay_s);
1547  }
1548  }
1549 
1550  {
1551  enum crm_ipc_flags ipc_flags = crm_ipc_flags_none;
1552 
1553  if (call_options & st_opt_sync_call) {
1554  pcmk__set_ipc_flags(ipc_flags, "stonith command",
1556  }
1557  rc = crm_ipc_send(native->ipc, op_msg, ipc_flags,
1558  1000 * (timeout + 60), &op_reply);
1559  }
1560  free_xml(op_msg);
1561 
1562  if (rc < 0) {
1563  crm_perror(LOG_ERR, "Couldn't perform %s operation (timeout=%ds): %d", op, timeout, rc);
1564  rc = -ECOMM;
1565  goto done;
1566  }
1567 
1568  crm_log_xml_trace(op_reply, "Reply");
1569 
1570  if (!(call_options & st_opt_sync_call)) {
1571  crm_trace("Async call %d, returning", stonith->call_id);
1572  free_xml(op_reply);
1573  return stonith->call_id;
1574  }
1575 
1576  rc = pcmk_ok;
1577  crm_element_value_int(op_reply, F_STONITH_CALLID, &reply_id);
1578 
1579  if (reply_id == stonith->call_id) {
1581 
1582  crm_trace("Synchronous reply %d received", reply_id);
1583 
1584  stonith__xe_get_result(op_reply, &result);
1587 
1588  if ((call_options & st_opt_discard_reply) || output_data == NULL) {
1589  crm_trace("Discarding reply");
1590 
1591  } else {
1592  *output_data = op_reply;
1593  op_reply = NULL; /* Prevent subsequent free */
1594  }
1595 
1596  } else if (reply_id <= 0) {
1597  crm_err("Received bad reply: No id set");
1598  crm_log_xml_err(op_reply, "Bad reply");
1599  free_xml(op_reply);
1600  rc = -ENOMSG;
1601 
1602  } else {
1603  crm_err("Received bad reply: %d (wanted %d)", reply_id, stonith->call_id);
1604  crm_log_xml_err(op_reply, "Old reply");
1605  free_xml(op_reply);
1606  rc = -ENOMSG;
1607  }
1608 
1609  done:
1610  if (crm_ipc_connected(native->ipc) == FALSE) {
1611  crm_err("Fencer disconnected");
1612  free(native->token); native->token = NULL;
1613  stonith->state = stonith_disconnected;
1614  }
1615 
1616  free_xml(op_reply);
1617  return rc;
1618 }
1619 
1620 /* Not used with mainloop */
1621 bool
1623 {
1624  gboolean stay_connected = TRUE;
1625  stonith_private_t *private = NULL;
1626 
1627  CRM_ASSERT(st != NULL);
1628  private = st->st_private;
1629 
1630  while (crm_ipc_ready(private->ipc)) {
1631 
1632  if (crm_ipc_read(private->ipc) > 0) {
1633  const char *msg = crm_ipc_buffer(private->ipc);
1634 
1635  stonith_dispatch_internal(msg, strlen(msg), st);
1636  }
1637 
1638  if (crm_ipc_connected(private->ipc) == FALSE) {
1639  crm_err("Connection closed");
1640  stay_connected = FALSE;
1641  }
1642  }
1643 
1644  return stay_connected;
1645 }
1646 
1647 static int
1648 stonith_api_free(stonith_t * stonith)
1649 {
1650  int rc = pcmk_ok;
1651 
1652  crm_trace("Destroying %p", stonith);
1653 
1654  if (stonith->state != stonith_disconnected) {
1655  crm_trace("Disconnecting %p first", stonith);
1656  rc = stonith->cmds->disconnect(stonith);
1657  }
1658 
1659  if (stonith->state == stonith_disconnected) {
1660  stonith_private_t *private = stonith->st_private;
1661 
1662  crm_trace("Removing %d callbacks", g_hash_table_size(private->stonith_op_callback_table));
1663  g_hash_table_destroy(private->stonith_op_callback_table);
1664 
1665  crm_trace("Destroying %d notification clients", g_list_length(private->notify_list));
1666  g_list_free_full(private->notify_list, free);
1667 
1668  free(stonith->st_private);
1669  free(stonith->cmds);
1670  free(stonith);
1671 
1672  } else {
1673  crm_err("Not free'ing active connection: %s (%d)", pcmk_strerror(rc), rc);
1674  }
1675 
1676  return rc;
1677 }
1678 
1679 void
1681 {
1682  crm_trace("Destroying %p", stonith);
1683  if(stonith) {
1684  stonith->cmds->free(stonith);
1685  }
1686 }
1687 
1688 static int
1689 stonith_api_validate(stonith_t *st, int call_options, const char *rsc_id,
1690  const char *namespace_s, const char *agent,
1691  stonith_key_value_t *params, int timeout, char **output,
1692  char **error_output)
1693 {
1694  /* Validation should be done directly via the agent, so we can get it from
1695  * stonith_admin when the cluster is not running, which is important for
1696  * higher-level tools.
1697  */
1698 
1699  int rc = pcmk_ok;
1700 
1701  /* Use a dummy node name in case the agent requires a target. We assume the
1702  * actual target doesn't matter for validation purposes (if in practice,
1703  * that is incorrect, we will need to allow the caller to pass the target).
1704  */
1705  const char *target = "node1";
1706  const char *host_arg = NULL;
1707 
1708  GHashTable *params_table = pcmk__strkey_table(free, free);
1709 
1710  // Convert parameter list to a hash table
1711  for (; params; params = params->next) {
1712  if (pcmk__str_eq(params->key, PCMK_STONITH_HOST_ARGUMENT,
1713  pcmk__str_casei)) {
1714  host_arg = params->value;
1715  }
1716  if (!pcmk_stonith_param(params->key)) {
1717  g_hash_table_insert(params_table, strdup(params->key),
1718  strdup(params->value));
1719  }
1720  }
1721 
1722 #if SUPPORT_CIBSECRETS
1723  rc = pcmk__substitute_secrets(rsc_id, params_table);
1724  if (rc != pcmk_rc_ok) {
1725  crm_warn("Could not replace secret parameters for validation of %s: %s",
1726  agent, pcmk_rc_str(rc));
1727  // rc is standard return value, don't return it in this function
1728  }
1729 #endif
1730 
1731  if (output) {
1732  *output = NULL;
1733  }
1734  if (error_output) {
1735  *error_output = NULL;
1736  }
1737 
1738  switch (stonith_get_namespace(agent, namespace_s)) {
1739  case st_namespace_rhcs:
1740  rc = stonith__rhcs_validate(st, call_options, target, agent,
1741  params_table, host_arg, timeout,
1742  output, error_output);
1743  break;
1744 
1745 #if HAVE_STONITH_STONITH_H
1746  case st_namespace_lha:
1747  rc = stonith__lha_validate(st, call_options, target, agent,
1748  params_table, timeout, output,
1749  error_output);
1750  break;
1751 #endif
1752 
1753  default:
1754  rc = -EINVAL;
1755  errno = EINVAL;
1756  crm_perror(LOG_ERR,
1757  "Agent %s not found or does not support validation",
1758  agent);
1759  break;
1760  }
1761  g_hash_table_destroy(params_table);
1762  return rc;
1763 }
1764 
1765 stonith_t *
1767 {
1768  stonith_t *new_stonith = NULL;
1769  stonith_private_t *private = NULL;
1770 
1771  new_stonith = calloc(1, sizeof(stonith_t));
1772  if (new_stonith == NULL) {
1773  return NULL;
1774  }
1775 
1776  private = calloc(1, sizeof(stonith_private_t));
1777  if (private == NULL) {
1778  free(new_stonith);
1779  return NULL;
1780  }
1781  new_stonith->st_private = private;
1782 
1783  private->stonith_op_callback_table = pcmk__intkey_table(stonith_destroy_op_callback);
1784  private->notify_list = NULL;
1785  private->notify_refcnt = 0;
1786  private->notify_deletes = FALSE;
1787 
1788  new_stonith->call_id = 1;
1789  new_stonith->state = stonith_disconnected;
1790 
1791  new_stonith->cmds = calloc(1, sizeof(stonith_api_operations_t));
1792  if (new_stonith->cmds == NULL) {
1793  free(new_stonith->st_private);
1794  free(new_stonith);
1795  return NULL;
1796  }
1797 
1798 /* *INDENT-OFF* */
1799  new_stonith->cmds->free = stonith_api_free;
1800  new_stonith->cmds->connect = stonith_api_signon;
1801  new_stonith->cmds->disconnect = stonith_api_signoff;
1802 
1803  new_stonith->cmds->list = stonith_api_list;
1804  new_stonith->cmds->monitor = stonith_api_monitor;
1805  new_stonith->cmds->status = stonith_api_status;
1806  new_stonith->cmds->fence = stonith_api_fence;
1807  new_stonith->cmds->fence_with_delay = stonith_api_fence_with_delay;
1808  new_stonith->cmds->confirm = stonith_api_confirm;
1809  new_stonith->cmds->history = stonith_api_history;
1810 
1811  new_stonith->cmds->list_agents = stonith_api_device_list;
1812  new_stonith->cmds->metadata = stonith_api_device_metadata;
1813 
1814  new_stonith->cmds->query = stonith_api_query;
1815  new_stonith->cmds->remove_device = stonith_api_remove_device;
1816  new_stonith->cmds->register_device = stonith_api_register_device;
1817 
1818  new_stonith->cmds->remove_level = stonith_api_remove_level;
1819  new_stonith->cmds->remove_level_full = stonith_api_remove_level_full;
1820  new_stonith->cmds->register_level = stonith_api_register_level;
1821  new_stonith->cmds->register_level_full = stonith_api_register_level_full;
1822 
1823  new_stonith->cmds->remove_callback = stonith_api_del_callback;
1824  new_stonith->cmds->register_callback = stonith_api_add_callback;
1825  new_stonith->cmds->remove_notification = stonith_api_del_notification;
1826  new_stonith->cmds->register_notification = stonith_api_add_notification;
1827 
1828  new_stonith->cmds->validate = stonith_api_validate;
1829 /* *INDENT-ON* */
1830 
1831  return new_stonith;
1832 }
1833 
1843 int
1844 stonith_api_connect_retry(stonith_t *st, const char *name, int max_attempts)
1845 {
1846  int rc = -EINVAL; // if max_attempts is not positive
1847 
1848  for (int attempt = 1; attempt <= max_attempts; attempt++) {
1849  rc = st->cmds->connect(st, name, NULL);
1850  if (rc == pcmk_ok) {
1851  return pcmk_ok;
1852  } else if (attempt < max_attempts) {
1853  crm_notice("Fencer connection attempt %d of %d failed (retrying in 2s): %s "
1854  CRM_XS " rc=%d",
1855  attempt, max_attempts, pcmk_strerror(rc), rc);
1856  sleep(2);
1857  }
1858  }
1859  crm_notice("Could not connect to fencer: %s " CRM_XS " rc=%d",
1860  pcmk_strerror(rc), rc);
1861  return rc;
1862 }
1863 
1865 stonith_key_value_add(stonith_key_value_t * head, const char *key, const char *value)
1866 {
1867  stonith_key_value_t *p, *end;
1868 
1869  p = calloc(1, sizeof(stonith_key_value_t));
1870  pcmk__str_update(&p->key, key);
1871  pcmk__str_update(&p->value, value);
1872 
1873  end = head;
1874  while (end && end->next) {
1875  end = end->next;
1876  }
1877 
1878  if (end) {
1879  end->next = p;
1880  } else {
1881  head = p;
1882  }
1883 
1884  return head;
1885 }
1886 
1887 void
1888 stonith_key_value_freeall(stonith_key_value_t * head, int keys, int values)
1889 {
1891 
1892  while (head) {
1893  p = head->next;
1894  if (keys) {
1895  free(head->key);
1896  }
1897  if (values) {
1898  free(head->value);
1899  }
1900  free(head);
1901  head = p;
1902  }
1903 }
1904 
1905 #define api_log_open() openlog("stonith-api", LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON)
1906 #define api_log(level, fmt, args...) syslog(level, "%s: "fmt, __func__, args)
1907 
1908 int
1909 stonith_api_kick(uint32_t nodeid, const char *uname, int timeout, bool off)
1910 {
1911  int rc = pcmk_ok;
1913  const char *action = off? "off" : "reboot";
1914 
1915  api_log_open();
1916  if (st == NULL) {
1917  api_log(LOG_ERR, "API initialization failed, could not kick (%s) node %u/%s",
1918  action, nodeid, uname);
1919  return -EPROTO;
1920  }
1921 
1922  rc = st->cmds->connect(st, "stonith-api", NULL);
1923  if (rc != pcmk_ok) {
1924  api_log(LOG_ERR, "Connection failed, could not kick (%s) node %u/%s : %s (%d)",
1925  action, nodeid, uname, pcmk_strerror(rc), rc);
1926  } else {
1927  char *name = (uname == NULL)? pcmk__itoa(nodeid) : strdup(uname);
1928  int opts = 0;
1929 
1932  if ((uname == NULL) && (nodeid > 0)) {
1934  }
1935  rc = st->cmds->fence(st, opts, name, action, timeout, 0);
1936  free(name);
1937 
1938  if (rc != pcmk_ok) {
1939  api_log(LOG_ERR, "Could not kick (%s) node %u/%s : %s (%d)",
1940  action, nodeid, uname, pcmk_strerror(rc), rc);
1941  } else {
1942  api_log(LOG_NOTICE, "Node %u/%s kicked: %s", nodeid, uname, action);
1943  }
1944  }
1945 
1947  return rc;
1948 }
1949 
1950 time_t
1951 stonith_api_time(uint32_t nodeid, const char *uname, bool in_progress)
1952 {
1953  int rc = pcmk_ok;
1954  time_t when = 0;
1956  stonith_history_t *history = NULL, *hp = NULL;
1957 
1958  if (st == NULL) {
1959  api_log(LOG_ERR, "Could not retrieve fence history for %u/%s: "
1960  "API initialization failed", nodeid, uname);
1961  return when;
1962  }
1963 
1964  rc = st->cmds->connect(st, "stonith-api", NULL);
1965  if (rc != pcmk_ok) {
1966  api_log(LOG_NOTICE, "Connection failed: %s (%d)", pcmk_strerror(rc), rc);
1967  } else {
1968  int entries = 0;
1969  int progress = 0;
1970  int completed = 0;
1971  int opts = 0;
1972  char *name = (uname == NULL)? pcmk__itoa(nodeid) : strdup(uname);
1973 
1975  if ((uname == NULL) && (nodeid > 0)) {
1977  }
1978  rc = st->cmds->history(st, opts, name, &history, 120);
1979  free(name);
1980 
1981  for (hp = history; hp; hp = hp->next) {
1982  entries++;
1983  if (in_progress) {
1984  progress++;
1985  if (hp->state != st_done && hp->state != st_failed) {
1986  when = time(NULL);
1987  }
1988 
1989  } else if (hp->state == st_done) {
1990  completed++;
1991  if (hp->completed > when) {
1992  when = hp->completed;
1993  }
1994  }
1995  }
1996 
1997  stonith_history_free(history);
1998 
1999  if(rc == pcmk_ok) {
2000  api_log(LOG_INFO, "Found %d entries for %u/%s: %d in progress, %d completed", entries, nodeid, uname, progress, completed);
2001  } else {
2002  api_log(LOG_ERR, "Could not retrieve fence history for %u/%s: %s (%d)", nodeid, uname, pcmk_strerror(rc), rc);
2003  }
2004  }
2005 
2007 
2008  if(when) {
2009  api_log(LOG_INFO, "Node %u/%s last kicked at: %ld", nodeid, uname, (long int)when);
2010  }
2011  return when;
2012 }
2013 
2014 bool
2015 stonith_agent_exists(const char *agent, int timeout)
2016 {
2017  stonith_t *st = NULL;
2018  stonith_key_value_t *devices = NULL;
2019  stonith_key_value_t *dIter = NULL;
2020  bool rc = FALSE;
2021 
2022  if (agent == NULL) {
2023  return rc;
2024  }
2025 
2026  st = stonith_api_new();
2027  if (st == NULL) {
2028  crm_err("Could not list fence agents: API memory allocation failed");
2029  return FALSE;
2030  }
2031  st->cmds->list_agents(st, st_opt_sync_call, NULL, &devices, timeout == 0 ? 120 : timeout);
2032 
2033  for (dIter = devices; dIter != NULL; dIter = dIter->next) {
2034  if (pcmk__str_eq(dIter->value, agent, pcmk__str_none)) {
2035  rc = TRUE;
2036  break;
2037  }
2038  }
2039 
2040  stonith_key_value_freeall(devices, 1, 1);
2042  return rc;
2043 }
2044 
2045 const char *
2047 {
2048  if (action == NULL) {
2049  return "fencing";
2050  } else if (!strcmp(action, "on")) {
2051  return "unfencing";
2052  } else if (!strcmp(action, "off")) {
2053  return "turning off";
2054  } else {
2055  return action;
2056  }
2057 }
2058 
2067 static void
2068 parse_list_line(const char *line, int len, GList **output)
2069 {
2070  size_t i = 0;
2071  size_t entry_start = 0;
2072 
2073  /* Skip complaints about additional parameters device doesn't understand
2074  *
2075  * @TODO Document or eliminate the implied restriction of target names
2076  */
2077  if (strstr(line, "invalid") || strstr(line, "variable")) {
2078  crm_debug("Skipping list output line: %s", line);
2079  return;
2080  }
2081 
2082  // Process line content, character by character
2083  for (i = 0; i <= len; i++) {
2084 
2085  if (isspace(line[i]) || (line[i] == ',') || (line[i] == ';')
2086  || (line[i] == '\0')) {
2087  // We've found a separator (i.e. the end of an entry)
2088 
2089  int rc = 0;
2090  char *entry = NULL;
2091 
2092  if (i == entry_start) {
2093  // Skip leading and sequential separators
2094  entry_start = i + 1;
2095  continue;
2096  }
2097 
2098  entry = calloc(i - entry_start + 1, sizeof(char));
2099  CRM_ASSERT(entry != NULL);
2100 
2101  /* Read entry, stopping at first separator
2102  *
2103  * @TODO Document or eliminate these character restrictions
2104  */
2105  rc = sscanf(line + entry_start, "%[a-zA-Z0-9_-.]", entry);
2106  if (rc != 1) {
2107  crm_warn("Could not parse list output entry: %s "
2108  CRM_XS " entry_start=%d position=%d",
2109  line + entry_start, entry_start, i);
2110  free(entry);
2111 
2112  } else if (pcmk__strcase_any_of(entry, "on", "off", NULL)) {
2113  /* Some agents print the target status in the list output,
2114  * though none are known now (the separate list-status command
2115  * is used for this, but it can also print "UNKNOWN"). To handle
2116  * this possibility, skip such entries.
2117  *
2118  * @TODO Document or eliminate the implied restriction of target
2119  * names.
2120  */
2121  free(entry);
2122 
2123  } else {
2124  // We have a valid entry
2125  *output = g_list_append(*output, entry);
2126  }
2127  entry_start = i + 1;
2128  }
2129  }
2130 }
2131 
2153 GList *
2154 stonith__parse_targets(const char *target_spec)
2155 {
2156  GList *targets = NULL;
2157 
2158  if (target_spec != NULL) {
2159  size_t out_len = strlen(target_spec);
2160  size_t line_start = 0; // Starting index of line being processed
2161 
2162  for (size_t i = 0; i <= out_len; ++i) {
2163  if ((target_spec[i] == '\n') || (target_spec[i] == '\0')
2164  || ((target_spec[i] == '\\') && (target_spec[i + 1] == 'n'))) {
2165  // We've reached the end of one line of output
2166 
2167  int len = i - line_start;
2168 
2169  if (len > 0) {
2170  char *line = strndup(target_spec + line_start, len);
2171 
2172  line[len] = '\0'; // Because it might be a newline
2173  parse_list_line(line, len, &targets);
2174  free(line);
2175  }
2176  if (target_spec[i] == '\\') {
2177  ++i; // backslash-n takes up two positions
2178  }
2179  line_start = i + 1;
2180  }
2181  }
2182  }
2183  return targets;
2184 }
2185 
2193 gboolean
2195 {
2196  gboolean ret = FALSE;
2197 
2198  for (stonith_history_t *prev_hp = top_history; prev_hp; prev_hp = prev_hp->next) {
2199  if (prev_hp == event) {
2200  break;
2201  }
2202 
2203  if ((prev_hp->state == st_done) &&
2204  pcmk__str_eq(event->target, prev_hp->target, pcmk__str_casei) &&
2205  pcmk__str_eq(event->action, prev_hp->action, pcmk__str_casei) &&
2206  pcmk__str_eq(event->delegate, prev_hp->delegate, pcmk__str_casei) &&
2207  ((event->completed < prev_hp->completed) ||
2208  ((event->completed == prev_hp->completed) && (event->completed_nsec < prev_hp->completed_nsec)))) {
2209  ret = TRUE;
2210  break;
2211  }
2212  }
2213  return ret;
2214 }
2215 
2227 {
2228  stonith_history_t *new = NULL, *pending = NULL, *hp, *np, *tmp;
2229 
2230  for (hp = history; hp; ) {
2231  tmp = hp->next;
2232  if ((hp->state == st_done) || (hp->state == st_failed)) {
2233  /* sort into new */
2234  if ((!new) || (hp->completed > new->completed) ||
2235  ((hp->completed == new->completed) && (hp->completed_nsec > new->completed_nsec))) {
2236  hp->next = new;
2237  new = hp;
2238  } else {
2239  np = new;
2240  do {
2241  if ((!np->next) || (hp->completed > np->next->completed) ||
2242  ((hp->completed == np->next->completed) && (hp->completed_nsec > np->next->completed_nsec))) {
2243  hp->next = np->next;
2244  np->next = hp;
2245  break;
2246  }
2247  np = np->next;
2248  } while (1);
2249  }
2250  } else {
2251  /* put into pending */
2252  hp->next = pending;
2253  pending = hp;
2254  }
2255  hp = tmp;
2256  }
2257 
2258  /* pending actions don't have a completed-stamp so make them go front */
2259  if (pending) {
2260  stonith_history_t *last_pending = pending;
2261 
2262  while (last_pending->next) {
2263  last_pending = last_pending->next;
2264  }
2265 
2266  last_pending->next = new;
2267  new = pending;
2268  }
2269  return new;
2270 }
2271 
2279 const char *
2281 {
2282  switch (state) {
2283  case st_query: return "querying";
2284  case st_exec: return "executing";
2285  case st_done: return "completed";
2286  case st_duplicate: return "duplicate";
2287  case st_failed: return "failed";
2288  }
2289  return "unknown";
2290 }
2291 
2294  bool (*matching_fn)(stonith_history_t *, void *),
2295  void *user_data)
2296 {
2297  for (stonith_history_t *hp = history; hp; hp = hp->next) {
2298  if (matching_fn(hp, user_data)) {
2299  return hp;
2300  }
2301  }
2302 
2303  return NULL;
2304 }
2305 
2306 bool
2308 {
2309  return history->state != st_failed && history->state != st_done;
2310 }
2311 
2312 bool
2313 stonith__event_state_eq(stonith_history_t *history, void *user_data)
2314 {
2315  return history->state == GPOINTER_TO_INT(user_data);
2316 }
2317 
2318 bool
2320 {
2321  return history->state != GPOINTER_TO_INT(user_data);
2322 }
2323 
2324 void
2325 stonith__device_parameter_flags(uint32_t *device_flags, const char *device_name,
2326  xmlNode *metadata)
2327 {
2328  xmlXPathObjectPtr xpath = NULL;
2329  int max = 0;
2330  int lpc = 0;
2331 
2332  CRM_CHECK((device_flags != NULL) && (metadata != NULL), return);
2333 
2334  xpath = xpath_search(metadata, "//parameter");
2335  max = numXpathResults(xpath);
2336 
2337  if (max <= 0) {
2338  freeXpathObject(xpath);
2339  return;
2340  }
2341 
2342  for (lpc = 0; lpc < max; lpc++) {
2343  const char *parameter = NULL;
2344  xmlNode *match = getXpathResult(xpath, lpc);
2345 
2346  CRM_LOG_ASSERT(match != NULL);
2347  if (match == NULL) {
2348  continue;
2349  }
2350 
2351  parameter = crm_element_value(match, "name");
2352 
2353  if (pcmk__str_eq(parameter, "plug", pcmk__str_casei)) {
2354  stonith__set_device_flags(*device_flags, device_name,
2356 
2357  } else if (pcmk__str_eq(parameter, "port", pcmk__str_casei)) {
2358  stonith__set_device_flags(*device_flags, device_name,
2360  }
2361  }
2362 
2363  freeXpathObject(xpath);
2364 }
2365 
2374 int
2376 {
2377  if ((data == NULL) || (data->opaque == NULL)) {
2378  return CRM_EX_ERROR;
2379  }
2380  return ((pcmk__action_result_t *) data->opaque)->exit_status;
2381 }
2382 
2391 int
2393 {
2394  if ((data == NULL) || (data->opaque == NULL)) {
2395  return PCMK_EXEC_UNKNOWN;
2396  }
2397  return ((pcmk__action_result_t *) data->opaque)->execution_status;
2398 }
2399 
2408 const char *
2410 {
2411  if ((data == NULL) || (data->opaque == NULL)) {
2412  return NULL;
2413  }
2414  return ((pcmk__action_result_t *) data->opaque)->exit_reason;
2415 }
2416 
2425 int
2427 {
2428  if ((event == NULL) || (event->opaque == NULL)) {
2429  return CRM_EX_ERROR;
2430  } else {
2431  struct event_private *event_private = event->opaque;
2432 
2433  return event_private->result.exit_status;
2434  }
2435 }
2436 
2445 int
2447 {
2448  if ((event == NULL) || (event->opaque == NULL)) {
2449  return PCMK_EXEC_UNKNOWN;
2450  } else {
2451  struct event_private *event_private = event->opaque;
2452 
2453  return event_private->result.execution_status;
2454  }
2455 }
2456 
2465 const char *
2467 {
2468  if ((event == NULL) || (event->opaque == NULL)) {
2469  return NULL;
2470  } else {
2471  struct event_private *event_private = event->opaque;
2472 
2473  return event_private->result.exit_reason;
2474  }
2475 }
2476 
2489 char *
2491 {
2492  const char *origin = event->client_origin;
2493  const char *executioner = event->executioner;
2494  const char *reason = stonith__event_exit_reason(event);
2495  const char *status;
2496 
2497  if (origin == NULL) {
2498  origin = "a client";
2499  }
2500  if (executioner == NULL) {
2501  executioner = "the cluster";
2502  }
2503 
2505  status = pcmk_exec_status_str(stonith__event_execution_status(event));
2506  } else if (stonith__event_exit_status(event) != CRM_EX_OK) {
2507  status = pcmk_exec_status_str(PCMK_EXEC_ERROR);
2508  } else {
2509  status = crm_exit_str(CRM_EX_OK);
2510  }
2511 
2512  return crm_strdup_printf("Operation %s of %s by %s for %s@%s: %s%s%s%s (ref=%s)",
2513  event->action, event->target, executioner, origin,
2514  event->origin, status,
2515  ((reason == NULL)? "" : " ("),
2516  ((reason == NULL)? "" : reason),
2517  ((reason == NULL)? "" : ")"),
2518  event->id);
2519 }
2520 
2521 
2522 // Deprecated functions kept only for backward API compatibility
2523 // LCOV_EXCL_START
2524 
2525 const char *get_stonith_provider(const char *agent, const char *provider);
2526 
2527 const char *
2528 get_stonith_provider(const char *agent, const char *provider)
2529 {
2530  return stonith_namespace2text(stonith_get_namespace(agent, provider));
2531 }
2532 
2533 // LCOV_EXCL_STOP
2534 // 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:741
#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:2325
stonith_t * stonith
Definition: st_client.c:79
#define api_log_open()
Definition: st_client.c:1905
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:2319
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:1865
void stonith_dump_pending_callbacks(stonith_t *stonith)
Definition: st_client.c:1339
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:2392
bool stonith__event_state_eq(stonith_history_t *history, void *user_data)
Definition: st_client.c:2313
#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:1909
#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:788
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:2046
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:2194
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:1888
#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:2466
#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:1951
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:2280
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:1844
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:1622
#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:2293
#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:2154
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:2426
#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:2015
#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:2490
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:2528
#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
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:1680
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:1906
const char * stonith__exit_reason(stonith_callback_data_t *data)
Definition: st_client.c:2409
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:2226
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:2446
#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:1766
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:2307
#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:2375
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