Blame SOURCES/003-fencing-reasons.patch

1b1151
From 8e6362cb2129bd56f817d449a195f3da87a545fa Mon Sep 17 00:00:00 2001
1b1151
From: Ken Gaillot <kgaillot@redhat.com>
1b1151
Date: Fri, 12 Nov 2021 14:28:56 -0600
1b1151
Subject: [PATCH 01/13] Refactor: libcrmcommon,fencer: convenience macro for
1b1151
 initializing results
1b1151
1b1151
for future reuse
1b1151
---
1b1151
 daemons/fenced/fenced_commands.c      | 14 ++------------
1b1151
 include/crm/common/results_internal.h | 15 +++++++++++++++
1b1151
 2 files changed, 17 insertions(+), 12 deletions(-)
1b1151
1b1151
diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c
1b1151
index 87600573e..9f2f1cc40 100644
1b1151
--- a/daemons/fenced/fenced_commands.c
1b1151
+++ b/daemons/fenced/fenced_commands.c
1b1151
@@ -388,12 +388,7 @@ static void
1b1151
 report_internal_result(async_command_t *cmd, int exit_status,
1b1151
                        int execution_status, const char *exit_reason)
1b1151
 {
1b1151
-    pcmk__action_result_t result = {
1b1151
-        // Ensure we don't pass garbage to free()
1b1151
-        .exit_reason = NULL,
1b1151
-        .action_stdout = NULL,
1b1151
-        .action_stderr = NULL
1b1151
-    };
1b1151
+    pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
1b1151
 
1b1151
     pcmk__set_result(&result, exit_status, execution_status, exit_reason);
1b1151
     cmd->done_cb(0, &result, cmd);
1b1151
@@ -2616,12 +2611,7 @@ stonith_fence_get_devices_cb(GList * devices, void *user_data)
1b1151
     }
1b1151
 
1b1151
     if (device == NULL) { // No device found
1b1151
-        pcmk__action_result_t result = {
1b1151
-            // Ensure we don't pass garbage to free()
1b1151
-            .exit_reason = NULL,
1b1151
-            .action_stdout = NULL,
1b1151
-            .action_stderr = NULL
1b1151
-        };
1b1151
+        pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
1b1151
 
1b1151
         pcmk__set_result(&result, CRM_EX_ERROR, PCMK_EXEC_NO_FENCE_DEVICE,
1b1151
                          "No fence device configured for target");
1b1151
diff --git a/include/crm/common/results_internal.h b/include/crm/common/results_internal.h
1b1151
index 804bf2a7a..6befaa0ed 100644
1b1151
--- a/include/crm/common/results_internal.h
1b1151
+++ b/include/crm/common/results_internal.h
1b1151
@@ -30,6 +30,21 @@ typedef struct {
1b1151
     char *action_stderr;    // Action error output
1b1151
 } pcmk__action_result_t;
1b1151
 
1b1151
+/*!
1b1151
+ * \internal
1b1151
+ * \brief Static initialization for an action result
1b1151
+ *
1b1151
+ * \note Importantly, this ensures pcmk__reset_result() won't try to free
1b1151
+ *       garbage.
1b1151
+ */
1b1151
+#define PCMK__UNKNOWN_RESULT {                  \
1b1151
+        .exit_status = CRM_EX_OK,               \
1b1151
+        .execution_status = PCMK_EXEC_UNKNOWN,  \
1b1151
+        .exit_reason = NULL,                    \
1b1151
+        .action_stdout = NULL,                  \
1b1151
+        .action_stderr = NULL,                  \
1b1151
+    }
1b1151
+
1b1151
 void pcmk__set_result(pcmk__action_result_t *result, int exit_status,
1b1151
                       enum pcmk_exec_status exec_status,
1b1151
                       const char *exit_reason);
1b1151
-- 
1b1151
2.27.0
1b1151
1b1151
1b1151
From 0937c92476ac737a5f5146932824bde8bdd7db98 Mon Sep 17 00:00:00 2001
1b1151
From: Ken Gaillot <kgaillot@redhat.com>
1b1151
Date: Fri, 12 Nov 2021 16:02:27 -0600
1b1151
Subject: [PATCH 02/13] Refactor: various: add convenience function for
1b1151
 checking result success
1b1151
1b1151
A successful pcmk__action_result_t has both exit status CRM_EX_OK (a.k.a
1b1151
PCMK_OCF_OK) and execution status PCMK_EXEC_DONE. Since checking that is
1b1151
clunky, we sometimes just check exit status, which is less than ideal.
1b1151
1b1151
The convenience function makes it easy to check both, and improves readability.
1b1151
---
1b1151
 daemons/controld/controld_remote_ra.c |  4 ++--
1b1151
 daemons/execd/execd_commands.c        | 12 ++++++------
1b1151
 daemons/fenced/fenced_commands.c      | 14 ++++++--------
1b1151
 include/crm/common/results_internal.h | 16 ++++++++++++++++
1b1151
 lib/fencing/st_client.c               |  4 ++--
1b1151
 lib/fencing/st_rhcs.c                 |  2 +-
1b1151
 6 files changed, 33 insertions(+), 19 deletions(-)
1b1151
1b1151
diff --git a/daemons/controld/controld_remote_ra.c b/daemons/controld/controld_remote_ra.c
1b1151
index 74cbfd673..55ac162c7 100644
1b1151
--- a/daemons/controld/controld_remote_ra.c
1b1151
+++ b/daemons/controld/controld_remote_ra.c
1b1151
@@ -297,7 +297,7 @@ static void
1b1151
 check_remote_node_state(remote_ra_cmd_t *cmd)
1b1151
 {
1b1151
     /* Only successful actions can change node state */
1b1151
-    if (cmd->result.exit_status != PCMK_OCF_OK) {
1b1151
+    if (!pcmk__result_ok(&(cmd->result))) {
1b1151
         return;
1b1151
     }
1b1151
 
1b1151
@@ -365,7 +365,7 @@ report_remote_ra_result(remote_ra_cmd_t * cmd)
1b1151
     lrmd__set_result(&op, cmd->result.exit_status, cmd->result.execution_status,
1b1151
                      cmd->result.exit_reason);
1b1151
 
1b1151
-    if (cmd->reported_success && (cmd->result.exit_status != PCMK_OCF_OK)) {
1b1151
+    if (cmd->reported_success && !pcmk__result_ok(&(cmd->result))) {
1b1151
         op.t_rcchange = (unsigned int) time(NULL);
1b1151
         /* This edge case will likely never ever occur, but if it does the
1b1151
          * result is that a failure will not be processed correctly. This is only
1b1151
diff --git a/daemons/execd/execd_commands.c b/daemons/execd/execd_commands.c
1b1151
index 667525039..02070bf11 100644
1b1151
--- a/daemons/execd/execd_commands.c
1b1151
+++ b/daemons/execd/execd_commands.c
1b1151
@@ -878,7 +878,7 @@ action_complete(svc_action_t * action)
1b1151
     }
1b1151
 
1b1151
     if (pcmk__str_eq(rclass, PCMK_RESOURCE_CLASS_SYSTEMD, pcmk__str_casei)) {
1b1151
-        if ((cmd->result.exit_status == PCMK_OCF_OK)
1b1151
+        if (pcmk__result_ok(&(cmd->result))
1b1151
             && pcmk__strcase_any_of(cmd->action, "start", "stop", NULL)) {
1b1151
             /* systemd returns from start and stop actions after the action
1b1151
              * begins, not after it completes. We have to jump through a few
1b1151
@@ -894,7 +894,7 @@ action_complete(svc_action_t * action)
1b1151
             if (cmd->result.execution_status == PCMK_EXEC_PENDING) {
1b1151
                 goagain = true;
1b1151
 
1b1151
-            } else if ((cmd->result.exit_status == PCMK_OCF_OK)
1b1151
+            } else if (pcmk__result_ok(&(cmd->result))
1b1151
                        && pcmk__str_eq(cmd->real_action, "stop", pcmk__str_casei)) {
1b1151
                 goagain = true;
1b1151
 
1b1151
@@ -927,12 +927,12 @@ action_complete(svc_action_t * action)
1b1151
 #if SUPPORT_NAGIOS
1b1151
     if (rsc && pcmk__str_eq(rsc->class, PCMK_RESOURCE_CLASS_NAGIOS, pcmk__str_casei)) {
1b1151
         if (action_matches(cmd, "monitor", 0)
1b1151
-            && (cmd->result.exit_status == PCMK_OCF_OK)) {
1b1151
+            && pcmk__result_ok(&(cmd->result))) {
1b1151
             /* Successfully executed --version for the nagios plugin */
1b1151
             cmd->result.exit_status = PCMK_OCF_NOT_RUNNING;
1b1151
 
1b1151
         } else if (pcmk__str_eq(cmd->action, "start", pcmk__str_casei)
1b1151
-                   && (cmd->result.exit_status != PCMK_OCF_OK)) {
1b1151
+                   && !pcmk__result_ok(&(cmd->result))) {
1b1151
 #ifdef PCMK__TIME_USE_CGT
1b1151
             goagain = true;
1b1151
 #endif
1b1151
@@ -955,7 +955,7 @@ action_complete(svc_action_t * action)
1b1151
             cmd->start_delay = delay;
1b1151
             cmd->timeout = timeout_left;
1b1151
 
1b1151
-            if (cmd->result.exit_status == PCMK_OCF_OK) {
1b1151
+            if (pcmk__result_ok(&(cmd->result))) {
1b1151
                 crm_debug("%s %s may still be in progress: re-scheduling (elapsed=%dms, remaining=%dms, start_delay=%dms)",
1b1151
                           cmd->rsc_id, cmd->real_action, time_sum, timeout_left, delay);
1b1151
 
1b1151
@@ -1066,7 +1066,7 @@ stonith_action_complete(lrmd_cmd_t * cmd, int rc)
1b1151
                                                          cmd->interval_ms, rc);
1b1151
 
1b1151
         // Certain successful actions change the known state of the resource
1b1151
-        if ((rsc != NULL) && (cmd->result.exit_status == PCMK_OCF_OK)) {
1b1151
+        if ((rsc != NULL) && pcmk__result_ok(&(cmd->result))) {
1b1151
             if (pcmk__str_eq(cmd->action, "start", pcmk__str_casei)) {
1b1151
                 rsc->st_probe_rc = pcmk_ok; // maps to PCMK_OCF_OK
1b1151
             } else if (pcmk__str_eq(cmd->action, "stop", pcmk__str_casei)) {
1b1151
diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c
1b1151
index 9f2f1cc40..26501a4b3 100644
1b1151
--- a/daemons/fenced/fenced_commands.c
1b1151
+++ b/daemons/fenced/fenced_commands.c
1b1151
@@ -1188,8 +1188,7 @@ dynamic_list_search_cb(int pid, const pcmk__action_result_t *result,
1b1151
 
1b1151
     mainloop_set_trigger(dev->work);
1b1151
 
1b1151
-    if ((result->execution_status == PCMK_EXEC_DONE)
1b1151
-        && (result->exit_status == CRM_EX_OK)) {
1b1151
+    if (pcmk__result_ok(result)) {
1b1151
         crm_info("Refreshing target list for %s", dev->id);
1b1151
         g_list_free_full(dev->targets, free);
1b1151
         dev->targets = stonith__parse_targets(result->action_stdout);
1b1151
@@ -2310,15 +2309,14 @@ log_async_result(async_command_t *cmd, const pcmk__action_result_t *result,
1b1151
     GString *msg = g_string_sized_new(80); // Reasonable starting size
1b1151
 
1b1151
     // Choose log levels appropriately if we have a result
1b1151
-    if ((result->execution_status == PCMK_EXEC_DONE)
1b1151
-        && (result->exit_status == CRM_EX_OK))  { // Success
1b1151
+    if (pcmk__result_ok(result)) {
1b1151
         log_level = (cmd->victim == NULL)? LOG_DEBUG : LOG_NOTICE;
1b1151
         if ((result->action_stdout != NULL)
1b1151
             && !pcmk__str_eq(cmd->action, "metadata", pcmk__str_casei)) {
1b1151
             output_log_level = LOG_DEBUG;
1b1151
         }
1b1151
         next = NULL;
1b1151
-    } else { // Failure
1b1151
+    } else {
1b1151
         log_level = (cmd->victim == NULL)? LOG_NOTICE : LOG_ERR;
1b1151
         if ((result->action_stdout != NULL)
1b1151
             && !pcmk__str_eq(cmd->action, "metadata", pcmk__str_casei)) {
1b1151
@@ -2482,7 +2480,7 @@ st_child_done(int pid, const pcmk__action_result_t *result, void *user_data)
1b1151
     /* The device is ready to do something else now */
1b1151
     device = g_hash_table_lookup(device_list, cmd->device);
1b1151
     if (device) {
1b1151
-        if (!device->verified && (result->exit_status == CRM_EX_OK) &&
1b1151
+        if (!device->verified && pcmk__result_ok(result) &&
1b1151
             (pcmk__strcase_any_of(cmd->action, "list", "monitor", "status", NULL))) {
1b1151
 
1b1151
             device->verified = TRUE;
1b1151
@@ -2491,7 +2489,7 @@ st_child_done(int pid, const pcmk__action_result_t *result, void *user_data)
1b1151
         mainloop_set_trigger(device->work);
1b1151
     }
1b1151
 
1b1151
-    if (result->exit_status == CRM_EX_OK) {
1b1151
+    if (pcmk__result_ok(result)) {
1b1151
         GList *iter;
1b1151
         /* see if there are any required devices left to execute for this op */
1b1151
         for (iter = cmd->device_next; iter != NULL; iter = iter->next) {
1b1151
@@ -2523,7 +2521,7 @@ st_child_done(int pid, const pcmk__action_result_t *result, void *user_data)
1b1151
 
1b1151
     send_async_reply(cmd, result, pid, false);
1b1151
 
1b1151
-    if (result->exit_status != CRM_EX_OK) {
1b1151
+    if (!pcmk__result_ok(result)) {
1b1151
         goto done;
1b1151
     }
1b1151
 
1b1151
diff --git a/include/crm/common/results_internal.h b/include/crm/common/results_internal.h
1b1151
index 6befaa0ed..0c5833937 100644
1b1151
--- a/include/crm/common/results_internal.h
1b1151
+++ b/include/crm/common/results_internal.h
1b1151
@@ -54,4 +54,20 @@ void pcmk__set_result_output(pcmk__action_result_t *result,
1b1151
 
1b1151
 void pcmk__reset_result(pcmk__action_result_t *result);
1b1151
 
1b1151
+/*!
1b1151
+ * \internal
1b1151
+ * \brief Check whether a result is OK
1b1151
+ *
1b1151
+ * \param[in] result
1b1151
+ *
1b1151
+ * \return true if the result's exit status is CRM_EX_OK and its
1b1151
+ *         execution status is PCMK_EXEC_DONE, otherwise false
1b1151
+ */
1b1151
+static inline bool
1b1151
+pcmk__result_ok(const pcmk__action_result_t *result)
1b1151
+{
1b1151
+    return (result != NULL) && (result->exit_status == CRM_EX_OK)
1b1151
+            && (result->execution_status == PCMK_EXEC_DONE);
1b1151
+}
1b1151
+
1b1151
 #endif // PCMK__COMMON_RESULTS_INTERNAL__H
1b1151
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
1b1151
index 2fbff7f24..af461d0d4 100644
1b1151
--- a/lib/fencing/st_client.c
1b1151
+++ b/lib/fencing/st_client.c
1b1151
@@ -760,7 +760,7 @@ stonith__result2rc(const pcmk__action_result_t *result)
1b1151
         default:                        break;
1b1151
     }
1b1151
 
1b1151
-    if (result->exit_status == CRM_EX_OK) {
1b1151
+    if (pcmk__result_ok(result)) {
1b1151
         return pcmk_rc_ok;
1b1151
     }
1b1151
 
1b1151
@@ -797,7 +797,7 @@ stonith_action_async_done(svc_action_t *svc_action)
1b1151
 
1b1151
     log_action(action, action->pid);
1b1151
 
1b1151
-    if ((action->result.exit_status != CRM_EX_OK)
1b1151
+    if (!pcmk__result_ok(&(action->result))
1b1151
         && update_remaining_timeout(action)) {
1b1151
 
1b1151
         int rc = internal_stonith_action_execute(action);
1b1151
diff --git a/lib/fencing/st_rhcs.c b/lib/fencing/st_rhcs.c
1b1151
index 6c8cbedc7..865e04bc2 100644
1b1151
--- a/lib/fencing/st_rhcs.c
1b1151
+++ b/lib/fencing/st_rhcs.c
1b1151
@@ -148,7 +148,7 @@ stonith__rhcs_get_metadata(const char *agent, int timeout, xmlNode **metadata)
1b1151
         return rc;
1b1151
     }
1b1151
 
1b1151
-    if (result->exit_status != CRM_EX_OK) {
1b1151
+    if (!pcmk__result_ok(result)) {
1b1151
         crm_warn("Metadata action for %s returned error code %d",
1b1151
                  agent, result->exit_status);
1b1151
         rc = pcmk_rc2legacy(stonith__result2rc(result));
1b1151
-- 
1b1151
2.27.0
1b1151
1b1151
1b1151
From 4c39ff00a0c028354a9da7f80986f7e34b05ba08 Mon Sep 17 00:00:00 2001
1b1151
From: Ken Gaillot <kgaillot@redhat.com>
1b1151
Date: Fri, 12 Nov 2021 16:07:01 -0600
1b1151
Subject: [PATCH 03/13] Low: fencing: improve mapping of execution status to
1b1151
 legacy return code
1b1151
1b1151
PCMK_EXEC_PENDING is likely not possible with the current code, but map it to
1b1151
EINPROGRESS for completeness.
1b1151
1b1151
PCMK_EXEC_INVALID is not yet used by the fencer but will be.
1b1151
---
1b1151
 lib/fencing/st_client.c | 30 ++++++++++++++++++++++++++----
1b1151
 1 file changed, 26 insertions(+), 4 deletions(-)
1b1151
1b1151
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
1b1151
index af461d0d4..93513e9f3 100644
1b1151
--- a/lib/fencing/st_client.c
1b1151
+++ b/lib/fencing/st_client.c
1b1151
@@ -749,7 +749,12 @@ update_remaining_timeout(stonith_action_t * action)
1b1151
 int
1b1151
 stonith__result2rc(const pcmk__action_result_t *result)
1b1151
 {
1b1151
+    if (pcmk__result_ok(result)) {
1b1151
+        return pcmk_rc_ok;
1b1151
+    }
1b1151
+
1b1151
     switch (result->execution_status) {
1b1151
+        case PCMK_EXEC_PENDING:         return EINPROGRESS;
1b1151
         case PCMK_EXEC_CANCELLED:       return ECANCELED;
1b1151
         case PCMK_EXEC_TIMEOUT:         return ETIME;
1b1151
         case PCMK_EXEC_NOT_INSTALLED:   return ENOENT;
1b1151
@@ -757,11 +762,28 @@ stonith__result2rc(const pcmk__action_result_t *result)
1b1151
         case PCMK_EXEC_NOT_CONNECTED:   return ENOTCONN;
1b1151
         case PCMK_EXEC_NO_FENCE_DEVICE: return ENODEV;
1b1151
         case PCMK_EXEC_NO_SECRETS:      return EACCES;
1b1151
-        default:                        break;
1b1151
-    }
1b1151
 
1b1151
-    if (pcmk__result_ok(result)) {
1b1151
-        return pcmk_rc_ok;
1b1151
+        /* For the fencing API, PCMK_EXEC_INVALID is used with fencer API
1b1151
+         * operations that don't involve executing an agent (for example,
1b1151
+         * registering devices). This allows us to use the CRM_EX_* codes in the
1b1151
+         * exit status for finer-grained responses.
1b1151
+         */
1b1151
+        case PCMK_EXEC_INVALID:
1b1151
+            switch (result->exit_status) {
1b1151
+                case CRM_EX_INSUFFICIENT_PRIV:  return EACCES;
1b1151
+                case CRM_EX_PROTOCOL:           return EPROTO;
1b1151
+
1b1151
+               /* CRM_EX_EXPIRED is used for orphaned fencing operations left
1b1151
+                * over from a previous instance of the fencer. For API backward
1b1151
+                * compatibility, this is mapped to the previously used code for
1b1151
+                * this case, EHOSTUNREACH.
1b1151
+                */
1b1151
+                case CRM_EX_EXPIRED:            return EHOSTUNREACH;
1b1151
+                default:                        break;
1b1151
+            }
1b1151
+
1b1151
+        default:
1b1151
+            break;
1b1151
     }
1b1151
 
1b1151
     // Try to provide useful error code based on result's error output
1b1151
-- 
1b1151
2.27.0
1b1151
1b1151
1b1151
From 4e638783d1cd7c9398a603fc6df7e9d868262b16 Mon Sep 17 00:00:00 2001
1b1151
From: Ken Gaillot <kgaillot@redhat.com>
1b1151
Date: Thu, 18 Nov 2021 11:41:12 -0600
1b1151
Subject: [PATCH 04/13] Refactor: libstonithd: separate action-related code
1b1151
 into own source file
1b1151
1b1151
Everything related to stonith_action_t has been moved from st_client.c to a new
1b1151
st_actions.c, since st_client.c was ridiculously large, and the action stuff
1b1151
isn't all client-related. No code was changed.
1b1151
1b1151
Before:
1b1151
   2804 st_client.c
1b1151
1b1151
After:
1b1151
   545 lib/fencing/st_actions.c
1b1151
  2278 lib/fencing/st_client.c
1b1151
---
1b1151
 lib/fencing/Makefile.am  |   2 +-
1b1151
 lib/fencing/st_actions.c | 545 +++++++++++++++++++++++++++++++++++++++
1b1151
 lib/fencing/st_client.c  | 528 +------------------------------------
1b1151
 3 files changed, 547 insertions(+), 528 deletions(-)
1b1151
 create mode 100644 lib/fencing/st_actions.c
1b1151
1b1151
diff --git a/lib/fencing/Makefile.am b/lib/fencing/Makefile.am
1b1151
index 205c4873d..dac215c16 100644
1b1151
--- a/lib/fencing/Makefile.am
1b1151
+++ b/lib/fencing/Makefile.am
1b1151
@@ -22,7 +22,7 @@ libstonithd_la_LDFLAGS	+= $(LDFLAGS_HARDENED_LIB)
1b1151
 libstonithd_la_LIBADD	= $(top_builddir)/lib/common/libcrmcommon.la
1b1151
 libstonithd_la_LIBADD   += $(top_builddir)/lib/services/libcrmservice.la
1b1151
 
1b1151
-libstonithd_la_SOURCES	= st_client.c st_output.c st_rhcs.c
1b1151
+libstonithd_la_SOURCES	= st_actions.c st_client.c st_output.c st_rhcs.c
1b1151
 if BUILD_LHA_SUPPORT
1b1151
 libstonithd_la_SOURCES	+= st_lha.c
1b1151
 endif
1b1151
diff --git a/lib/fencing/st_actions.c b/lib/fencing/st_actions.c
1b1151
new file mode 100644
1b1151
index 000000000..64d3afd5d
1b1151
--- /dev/null
1b1151
+++ b/lib/fencing/st_actions.c
1b1151
@@ -0,0 +1,545 @@
1b1151
+/*
1b1151
+ * Copyright 2004-2021 the Pacemaker project contributors
1b1151
+ *
1b1151
+ * The version control history for this file may have further details.
1b1151
+ *
1b1151
+ * This source code is licensed under the GNU Lesser General Public License
1b1151
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
1b1151
+ */
1b1151
+
1b1151
+#include <crm_internal.h>
1b1151
+
1b1151
+#include <stdlib.h>
1b1151
+#include <stdio.h>
1b1151
+#include <string.h>
1b1151
+#include <libgen.h>
1b1151
+#include <inttypes.h>
1b1151
+#include <sys/types.h>
1b1151
+#include <glib.h>
1b1151
+
1b1151
+#include <crm/crm.h>
1b1151
+#include <crm/stonith-ng.h>
1b1151
+#include <crm/fencing/internal.h>
1b1151
+#include <crm/msg_xml.h>
1b1151
+#include <crm/services_internal.h>
1b1151
+
1b1151
+#include "fencing_private.h"
1b1151
+
1b1151
+struct stonith_action_s {
1b1151
+    /*! user defined data */
1b1151
+    char *agent;
1b1151
+    char *action;
1b1151
+    char *victim;
1b1151
+    GHashTable *args;
1b1151
+    int timeout;
1b1151
+    int async;
1b1151
+    void *userdata;
1b1151
+    void (*done_cb) (int pid, const pcmk__action_result_t *result,
1b1151
+                     void *user_data);
1b1151
+    void (*fork_cb) (int pid, void *user_data);
1b1151
+
1b1151
+    svc_action_t *svc_action;
1b1151
+
1b1151
+    /*! internal timing information */
1b1151
+    time_t initial_start_time;
1b1151
+    int tries;
1b1151
+    int remaining_timeout;
1b1151
+    int max_retries;
1b1151
+
1b1151
+    int pid;
1b1151
+    pcmk__action_result_t result;
1b1151
+};
1b1151
+
1b1151
+static int internal_stonith_action_execute(stonith_action_t *action);
1b1151
+static void log_action(stonith_action_t *action, pid_t pid);
1b1151
+
1b1151
+/*!
1b1151
+ * \internal
1b1151
+ * \brief Set an action's result based on services library result
1b1151
+ *
1b1151
+ * \param[in] action      Fence action to set result for
1b1151
+ * \param[in] svc_action  Service action to get result from
1b1151
+ */
1b1151
+static void
1b1151
+set_result_from_svc_action(stonith_action_t *action, svc_action_t *svc_action)
1b1151
+{
1b1151
+    pcmk__set_result(&(action->result), svc_action->rc, svc_action->status,
1b1151
+                     services__exit_reason(svc_action));
1b1151
+    pcmk__set_result_output(&(action->result),
1b1151
+                            services__grab_stdout(svc_action),
1b1151
+                            services__grab_stderr(svc_action));
1b1151
+}
1b1151
+
1b1151
+static void
1b1151
+log_action(stonith_action_t *action, pid_t pid)
1b1151
+{
1b1151
+    /* The services library has already logged the output at info or debug
1b1151
+     * level, so just raise to warning for stderr.
1b1151
+     */
1b1151
+    if (action->result.action_stderr != NULL) {
1b1151
+        /* Logging the whole string confuses syslog when the string is xml */
1b1151
+        char *prefix = crm_strdup_printf("%s[%d] stderr:", action->agent, pid);
1b1151
+
1b1151
+        crm_log_output(LOG_WARNING, prefix, action->result.action_stderr);
1b1151
+        free(prefix);
1b1151
+    }
1b1151
+}
1b1151
+
1b1151
+static void
1b1151
+append_config_arg(gpointer key, gpointer value, gpointer user_data)
1b1151
+{
1b1151
+    /* The fencer will filter "action" out when it registers the device,
1b1151
+     * but ignore it here in case any external API users don't.
1b1151
+     *
1b1151
+     * Also filter out parameters handled directly by Pacemaker.
1b1151
+     */
1b1151
+    if (!pcmk__str_eq(key, STONITH_ATTR_ACTION_OP, pcmk__str_casei)
1b1151
+        && !pcmk_stonith_param(key)
1b1151
+        && (strstr(key, CRM_META) == NULL)
1b1151
+        && !pcmk__str_eq(key, "crm_feature_set", pcmk__str_casei)) {
1b1151
+
1b1151
+        crm_trace("Passing %s=%s with fence action",
1b1151
+                  (const char *) key, (const char *) (value? value : ""));
1b1151
+        g_hash_table_insert((GHashTable *) user_data,
1b1151
+                            strdup(key), strdup(value? value : ""));
1b1151
+    }
1b1151
+}
1b1151
+
1b1151
+static GHashTable *
1b1151
+make_args(const char *agent, const char *action, const char *victim,
1b1151
+          uint32_t victim_nodeid, GHashTable * device_args,
1b1151
+          GHashTable * port_map, const char *host_arg)
1b1151
+{
1b1151
+    GHashTable *arg_list = NULL;
1b1151
+    const char *value = NULL;
1b1151
+
1b1151
+    CRM_CHECK(action != NULL, return NULL);
1b1151
+
1b1151
+    arg_list = pcmk__strkey_table(free, free);
1b1151
+
1b1151
+    // Add action to arguments (using an alias if requested)
1b1151
+    if (device_args) {
1b1151
+        char buffer[512];
1b1151
+
1b1151
+        snprintf(buffer, sizeof(buffer), "pcmk_%s_action", action);
1b1151
+        value = g_hash_table_lookup(device_args, buffer);
1b1151
+        if (value) {
1b1151
+            crm_debug("Substituting '%s' for fence action %s targeting %s",
1b1151
+                      value, action, victim);
1b1151
+            action = value;
1b1151
+        }
1b1151
+    }
1b1151
+    g_hash_table_insert(arg_list, strdup(STONITH_ATTR_ACTION_OP),
1b1151
+                        strdup(action));
1b1151
+
1b1151
+    /* If this is a fencing operation against another node, add more standard
1b1151
+     * arguments.
1b1151
+     */
1b1151
+    if (victim && device_args) {
1b1151
+        const char *param = NULL;
1b1151
+
1b1151
+        /* Always pass the target's name, per
1b1151
+         * https://github.com/ClusterLabs/fence-agents/blob/master/doc/FenceAgentAPI.md
1b1151
+         */
1b1151
+        g_hash_table_insert(arg_list, strdup("nodename"), strdup(victim));
1b1151
+
1b1151
+        // If the target's node ID was specified, pass it, too
1b1151
+        if (victim_nodeid) {
1b1151
+            char *nodeid = crm_strdup_printf("%" PRIu32, victim_nodeid);
1b1151
+
1b1151
+            // cts-fencing looks for this log message
1b1151
+            crm_info("Passing '%s' as nodeid with fence action '%s' targeting %s",
1b1151
+                     nodeid, action, victim);
1b1151
+            g_hash_table_insert(arg_list, strdup("nodeid"), nodeid);
1b1151
+        }
1b1151
+
1b1151
+        // Check whether target must be specified in some other way
1b1151
+        param = g_hash_table_lookup(device_args, PCMK_STONITH_HOST_ARGUMENT);
1b1151
+        if (!pcmk__str_eq(agent, "fence_legacy", pcmk__str_none)
1b1151
+            && !pcmk__str_eq(param, "none", pcmk__str_casei)) {
1b1151
+
1b1151
+            if (param == NULL) {
1b1151
+                /* Use the caller's default for pcmk_host_argument, or "port" if
1b1151
+                 * none was given
1b1151
+                 */
1b1151
+                param = (host_arg == NULL)? "port" : host_arg;
1b1151
+            }
1b1151
+            value = g_hash_table_lookup(device_args, param);
1b1151
+
1b1151
+            if (pcmk__str_eq(value, "dynamic",
1b1151
+                             pcmk__str_casei|pcmk__str_null_matches)) {
1b1151
+                /* If the host argument was "dynamic" or not explicitly specified,
1b1151
+                 * add it with the target
1b1151
+                 */
1b1151
+                const char *alias = NULL;
1b1151
+
1b1151
+                if (port_map) {
1b1151
+                    alias = g_hash_table_lookup(port_map, victim);
1b1151
+                }
1b1151
+                if (alias == NULL) {
1b1151
+                    alias = victim;
1b1151
+                }
1b1151
+                crm_debug("Passing %s='%s' with fence action %s targeting %s",
1b1151
+                          param, alias, action, victim);
1b1151
+                g_hash_table_insert(arg_list, strdup(param), strdup(alias));
1b1151
+            }
1b1151
+        }
1b1151
+    }
1b1151
+
1b1151
+    if (device_args) {
1b1151
+        g_hash_table_foreach(device_args, append_config_arg, arg_list);
1b1151
+    }
1b1151
+
1b1151
+    return arg_list;
1b1151
+}
1b1151
+
1b1151
+/*!
1b1151
+ * \internal
1b1151
+ * \brief Free all memory used by a stonith action
1b1151
+ *
1b1151
+ * \param[in,out] action  Action to free
1b1151
+ */
1b1151
+void
1b1151
+stonith__destroy_action(stonith_action_t *action)
1b1151
+{
1b1151
+    if (action) {
1b1151
+        free(action->agent);
1b1151
+        if (action->args) {
1b1151
+            g_hash_table_destroy(action->args);
1b1151
+        }
1b1151
+        free(action->action);
1b1151
+        free(action->victim);
1b1151
+        if (action->svc_action) {
1b1151
+            services_action_free(action->svc_action);
1b1151
+        }
1b1151
+        pcmk__reset_result(&(action->result));
1b1151
+        free(action);
1b1151
+    }
1b1151
+}
1b1151
+
1b1151
+/*!
1b1151
+ * \internal
1b1151
+ * \brief Get the result of an executed stonith action
1b1151
+ *
1b1151
+ * \param[in] action  Executed action
1b1151
+ *
1b1151
+ * \return Pointer to action's result (or NULL if \p action is NULL)
1b1151
+ */
1b1151
+pcmk__action_result_t *
1b1151
+stonith__action_result(stonith_action_t *action)
1b1151
+{
1b1151
+    return (action == NULL)? NULL : &(action->result);
1b1151
+}
1b1151
+
1b1151
+#define FAILURE_MAX_RETRIES 2
1b1151
+stonith_action_t *
1b1151
+stonith_action_create(const char *agent,
1b1151
+                      const char *_action,
1b1151
+                      const char *victim,
1b1151
+                      uint32_t victim_nodeid,
1b1151
+                      int timeout, GHashTable * device_args,
1b1151
+                      GHashTable * port_map, const char *host_arg)
1b1151
+{
1b1151
+    stonith_action_t *action;
1b1151
+
1b1151
+    action = calloc(1, sizeof(stonith_action_t));
1b1151
+    action->args = make_args(agent, _action, victim, victim_nodeid,
1b1151
+                             device_args, port_map, host_arg);
1b1151
+    crm_debug("Preparing '%s' action for %s using agent %s",
1b1151
+              _action, (victim? victim : "no target"), agent);
1b1151
+    action->agent = strdup(agent);
1b1151
+    action->action = strdup(_action);
1b1151
+    if (victim) {
1b1151
+        action->victim = strdup(victim);
1b1151
+    }
1b1151
+    action->timeout = action->remaining_timeout = timeout;
1b1151
+    action->max_retries = FAILURE_MAX_RETRIES;
1b1151
+
1b1151
+    pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN, PCMK_EXEC_UNKNOWN,
1b1151
+                     "Initialization bug in fencing library");
1b1151
+
1b1151
+    if (device_args) {
1b1151
+        char buffer[512];
1b1151
+        const char *value = NULL;
1b1151
+
1b1151
+        snprintf(buffer, sizeof(buffer), "pcmk_%s_retries", _action);
1b1151
+        value = g_hash_table_lookup(device_args, buffer);
1b1151
+
1b1151
+        if (value) {
1b1151
+            action->max_retries = atoi(value);
1b1151
+        }
1b1151
+    }
1b1151
+
1b1151
+    return action;
1b1151
+}
1b1151
+
1b1151
+static gboolean
1b1151
+update_remaining_timeout(stonith_action_t * action)
1b1151
+{
1b1151
+    int diff = time(NULL) - action->initial_start_time;
1b1151
+
1b1151
+    if (action->tries >= action->max_retries) {
1b1151
+        crm_info("Attempted to execute agent %s (%s) the maximum number of times (%d) allowed",
1b1151
+                 action->agent, action->action, action->max_retries);
1b1151
+        action->remaining_timeout = 0;
1b1151
+    } else if ((action->result.execution_status != PCMK_EXEC_TIMEOUT)
1b1151
+               && (diff < (action->timeout * 0.7))) {
1b1151
+        /* only set remaining timeout period if there is 30%
1b1151
+         * or greater of the original timeout period left */
1b1151
+        action->remaining_timeout = action->timeout - diff;
1b1151
+    } else {
1b1151
+        action->remaining_timeout = 0;
1b1151
+    }
1b1151
+    return action->remaining_timeout ? TRUE : FALSE;
1b1151
+}
1b1151
+
1b1151
+/*!
1b1151
+ * \internal
1b1151
+ * \brief Map a fencing action result to a standard return code
1b1151
+ *
1b1151
+ * \param[in] result  Fencing action result to map
1b1151
+ *
1b1151
+ * \return Standard Pacemaker return code that best corresponds to \p result
1b1151
+ */
1b1151
+int
1b1151
+stonith__result2rc(const pcmk__action_result_t *result)
1b1151
+{
1b1151
+    if (pcmk__result_ok(result)) {
1b1151
+        return pcmk_rc_ok;
1b1151
+    }
1b1151
+
1b1151
+    switch (result->execution_status) {
1b1151
+        case PCMK_EXEC_PENDING:         return EINPROGRESS;
1b1151
+        case PCMK_EXEC_CANCELLED:       return ECANCELED;
1b1151
+        case PCMK_EXEC_TIMEOUT:         return ETIME;
1b1151
+        case PCMK_EXEC_NOT_INSTALLED:   return ENOENT;
1b1151
+        case PCMK_EXEC_NOT_SUPPORTED:   return EOPNOTSUPP;
1b1151
+        case PCMK_EXEC_NOT_CONNECTED:   return ENOTCONN;
1b1151
+        case PCMK_EXEC_NO_FENCE_DEVICE: return ENODEV;
1b1151
+        case PCMK_EXEC_NO_SECRETS:      return EACCES;
1b1151
+
1b1151
+        /* For the fencing API, PCMK_EXEC_INVALID is used with fencer API
1b1151
+         * operations that don't involve executing an agent (for example,
1b1151
+         * registering devices). This allows us to use the CRM_EX_* codes in the
1b1151
+         * exit status for finer-grained responses.
1b1151
+         */
1b1151
+        case PCMK_EXEC_INVALID:
1b1151
+            switch (result->exit_status) {
1b1151
+                case CRM_EX_INSUFFICIENT_PRIV:  return EACCES;
1b1151
+                case CRM_EX_PROTOCOL:           return EPROTO;
1b1151
+
1b1151
+               /* CRM_EX_EXPIRED is used for orphaned fencing operations left
1b1151
+                * over from a previous instance of the fencer. For API backward
1b1151
+                * compatibility, this is mapped to the previously used code for
1b1151
+                * this case, EHOSTUNREACH.
1b1151
+                */
1b1151
+                case CRM_EX_EXPIRED:            return EHOSTUNREACH;
1b1151
+                default:                        break;
1b1151
+            }
1b1151
+
1b1151
+        default:
1b1151
+            break;
1b1151
+    }
1b1151
+
1b1151
+    // Try to provide useful error code based on result's error output
1b1151
+
1b1151
+    if (result->action_stderr == NULL) {
1b1151
+        return ENODATA;
1b1151
+
1b1151
+    } else if (strcasestr(result->action_stderr, "timed out")
1b1151
+               || strcasestr(result->action_stderr, "timeout")) {
1b1151
+        return ETIME;
1b1151
+
1b1151
+    } else if (strcasestr(result->action_stderr, "unrecognised action")
1b1151
+               || strcasestr(result->action_stderr, "unrecognized action")
1b1151
+               || strcasestr(result->action_stderr, "unsupported action")) {
1b1151
+        return EOPNOTSUPP;
1b1151
+    }
1b1151
+
1b1151
+    // Oh well, we tried
1b1151
+    return pcmk_rc_error;
1b1151
+}
1b1151
+
1b1151
+static void
1b1151
+stonith_action_async_done(svc_action_t *svc_action)
1b1151
+{
1b1151
+    stonith_action_t *action = (stonith_action_t *) svc_action->cb_data;
1b1151
+
1b1151
+    set_result_from_svc_action(action, svc_action);
1b1151
+
1b1151
+    svc_action->params = NULL;
1b1151
+
1b1151
+    crm_debug("Child process %d performing action '%s' exited with rc %d",
1b1151
+                action->pid, action->action, svc_action->rc);
1b1151
+
1b1151
+    log_action(action, action->pid);
1b1151
+
1b1151
+    if (!pcmk__result_ok(&(action->result))
1b1151
+        && update_remaining_timeout(action)) {
1b1151
+
1b1151
+        int rc = internal_stonith_action_execute(action);
1b1151
+        if (rc == pcmk_ok) {
1b1151
+            return;
1b1151
+        }
1b1151
+    }
1b1151
+
1b1151
+    if (action->done_cb) {
1b1151
+        action->done_cb(action->pid, &(action->result), action->userdata);
1b1151
+    }
1b1151
+
1b1151
+    action->svc_action = NULL; // don't remove our caller
1b1151
+    stonith__destroy_action(action);
1b1151
+}
1b1151
+
1b1151
+static void
1b1151
+stonith_action_async_forked(svc_action_t *svc_action)
1b1151
+{
1b1151
+    stonith_action_t *action = (stonith_action_t *) svc_action->cb_data;
1b1151
+
1b1151
+    action->pid = svc_action->pid;
1b1151
+    action->svc_action = svc_action;
1b1151
+
1b1151
+    if (action->fork_cb) {
1b1151
+        (action->fork_cb) (svc_action->pid, action->userdata);
1b1151
+    }
1b1151
+
1b1151
+    crm_trace("Child process %d performing action '%s' successfully forked",
1b1151
+              action->pid, action->action);
1b1151
+}
1b1151
+
1b1151
+static int
1b1151
+internal_stonith_action_execute(stonith_action_t * action)
1b1151
+{
1b1151
+    int rc = -EPROTO;
1b1151
+    int is_retry = 0;
1b1151
+    svc_action_t *svc_action = NULL;
1b1151
+    static int stonith_sequence = 0;
1b1151
+    char *buffer = NULL;
1b1151
+
1b1151
+    CRM_CHECK(action != NULL, return -EINVAL);
1b1151
+
1b1151
+    if ((action->action == NULL) || (action->args == NULL)
1b1151
+        || (action->agent == NULL)) {
1b1151
+        pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN_ERROR,
1b1151
+                         PCMK_EXEC_ERROR_FATAL, "Bug in fencing library");
1b1151
+        return -EINVAL;
1b1151
+    }
1b1151
+
1b1151
+    if (!action->tries) {
1b1151
+        action->initial_start_time = time(NULL);
1b1151
+    }
1b1151
+    action->tries++;
1b1151
+
1b1151
+    if (action->tries > 1) {
1b1151
+        crm_info("Attempt %d to execute %s (%s). remaining timeout is %d",
1b1151
+                 action->tries, action->agent, action->action, action->remaining_timeout);
1b1151
+        is_retry = 1;
1b1151
+    }
1b1151
+
1b1151
+    buffer = crm_strdup_printf(PCMK__FENCE_BINDIR "/%s",
1b1151
+                               basename(action->agent));
1b1151
+    svc_action = services_action_create_generic(buffer, NULL);
1b1151
+    free(buffer);
1b1151
+
1b1151
+    if (svc_action->rc != PCMK_OCF_UNKNOWN) {
1b1151
+        set_result_from_svc_action(action, svc_action);
1b1151
+        services_action_free(svc_action);
1b1151
+        return -E2BIG;
1b1151
+    }
1b1151
+
1b1151
+    svc_action->timeout = 1000 * action->remaining_timeout;
1b1151
+    svc_action->standard = strdup(PCMK_RESOURCE_CLASS_STONITH);
1b1151
+    svc_action->id = crm_strdup_printf("%s_%s_%d", basename(action->agent),
1b1151
+                                       action->action, action->tries);
1b1151
+    svc_action->agent = strdup(action->agent);
1b1151
+    svc_action->sequence = stonith_sequence++;
1b1151
+    svc_action->params = action->args;
1b1151
+    svc_action->cb_data = (void *) action;
1b1151
+    svc_action->flags = pcmk__set_flags_as(__func__, __LINE__,
1b1151
+                                           LOG_TRACE, "Action",
1b1151
+                                           svc_action->id, svc_action->flags,
1b1151
+                                           SVC_ACTION_NON_BLOCKED,
1b1151
+                                           "SVC_ACTION_NON_BLOCKED");
1b1151
+
1b1151
+    /* keep retries from executing out of control and free previous results */
1b1151
+    if (is_retry) {
1b1151
+        pcmk__reset_result(&(action->result));
1b1151
+        sleep(1);
1b1151
+    }
1b1151
+
1b1151
+    if (action->async) {
1b1151
+        /* async */
1b1151
+        if (services_action_async_fork_notify(svc_action,
1b1151
+                                              &stonith_action_async_done,
1b1151
+                                              &stonith_action_async_forked)) {
1b1151
+            pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN,
1b1151
+                             PCMK_EXEC_PENDING, NULL);
1b1151
+            return pcmk_ok;
1b1151
+        }
1b1151
+
1b1151
+    } else if (services_action_sync(svc_action)) { // sync success
1b1151
+        rc = pcmk_ok;
1b1151
+
1b1151
+    } else { // sync failure
1b1151
+        rc = -ECONNABORTED;
1b1151
+    }
1b1151
+
1b1151
+    set_result_from_svc_action(action, svc_action);
1b1151
+    svc_action->params = NULL;
1b1151
+    services_action_free(svc_action);
1b1151
+    return rc;
1b1151
+}
1b1151
+
1b1151
+/*!
1b1151
+ * \internal
1b1151
+ * \brief Kick off execution of an async stonith action
1b1151
+ *
1b1151
+ * \param[in,out] action        Action to be executed
1b1151
+ * \param[in,out] userdata      Datapointer to be passed to callbacks
1b1151
+ * \param[in]     done          Callback to notify action has failed/succeeded
1b1151
+ * \param[in]     fork_callback Callback to notify successful fork of child
1b1151
+ *
1b1151
+ * \return pcmk_ok if ownership of action has been taken, -errno otherwise
1b1151
+ */
1b1151
+int
1b1151
+stonith_action_execute_async(stonith_action_t * action,
1b1151
+                             void *userdata,
1b1151
+                             void (*done) (int pid,
1b1151
+                                           const pcmk__action_result_t *result,
1b1151
+                                           void *user_data),
1b1151
+                             void (*fork_cb) (int pid, void *user_data))
1b1151
+{
1b1151
+    if (!action) {
1b1151
+        return -EINVAL;
1b1151
+    }
1b1151
+
1b1151
+    action->userdata = userdata;
1b1151
+    action->done_cb = done;
1b1151
+    action->fork_cb = fork_cb;
1b1151
+    action->async = 1;
1b1151
+
1b1151
+    return internal_stonith_action_execute(action);
1b1151
+}
1b1151
+
1b1151
+/*!
1b1151
+ * \internal
1b1151
+ * \brief Execute a stonith action
1b1151
+ *
1b1151
+ * \param[in,out] action  Action to execute
1b1151
+ *
1b1151
+ * \return pcmk_ok on success, -errno otherwise
1b1151
+ */
1b1151
+int
1b1151
+stonith__execute(stonith_action_t *action)
1b1151
+{
1b1151
+    int rc = pcmk_ok;
1b1151
+
1b1151
+    CRM_CHECK(action != NULL, return -EINVAL);
1b1151
+
1b1151
+    // Keep trying until success, max retries, or timeout
1b1151
+    do {
1b1151
+        rc = internal_stonith_action_execute(action);
1b1151
+    } while ((rc != pcmk_ok) && update_remaining_timeout(action));
1b1151
+
1b1151
+    return rc;
1b1151
+}
1b1151
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
1b1151
index 93513e9f3..944cd1863 100644
1b1151
--- a/lib/fencing/st_client.c
1b1151
+++ b/lib/fencing/st_client.c
1b1151
@@ -8,28 +8,20 @@
1b1151
  */
1b1151
 
1b1151
 #include <crm_internal.h>
1b1151
-#include <unistd.h>
1b1151
+
1b1151
 #include <stdlib.h>
1b1151
 #include <stdio.h>
1b1151
 #include <stdbool.h>
1b1151
 #include <string.h>
1b1151
 #include <ctype.h>
1b1151
-#include <libgen.h>
1b1151
 #include <inttypes.h>
1b1151
-
1b1151
-#include <sys/stat.h>
1b1151
 #include <sys/types.h>
1b1151
-#include <sys/wait.h>
1b1151
-
1b1151
 #include <glib.h>
1b1151
 
1b1151
 #include <crm/crm.h>
1b1151
 #include <crm/stonith-ng.h>
1b1151
 #include <crm/fencing/internal.h>
1b1151
 #include <crm/msg_xml.h>
1b1151
-#include <crm/common/xml.h>
1b1151
-#include <crm/common/xml_internal.h>
1b1151
-#include <crm/services_internal.h>
1b1151
 
1b1151
 #include <crm/common/mainloop.h>
1b1151
 
1b1151
@@ -37,31 +29,6 @@
1b1151
 
1b1151
 CRM_TRACE_INIT_DATA(stonith);
1b1151
 
1b1151
-struct stonith_action_s {
1b1151
-    /*! user defined data */
1b1151
-    char *agent;
1b1151
-    char *action;
1b1151
-    char *victim;
1b1151
-    GHashTable *args;
1b1151
-    int timeout;
1b1151
-    int async;
1b1151
-    void *userdata;
1b1151
-    void (*done_cb) (int pid, const pcmk__action_result_t *result,
1b1151
-                     void *user_data);
1b1151
-    void (*fork_cb) (int pid, void *user_data);
1b1151
-
1b1151
-    svc_action_t *svc_action;
1b1151
-
1b1151
-    /*! internal timing information */
1b1151
-    time_t initial_start_time;
1b1151
-    int tries;
1b1151
-    int remaining_timeout;
1b1151
-    int max_retries;
1b1151
-
1b1151
-    int pid;
1b1151
-    pcmk__action_result_t result;
1b1151
-};
1b1151
-
1b1151
 typedef struct stonith_private_s {
1b1151
     char *token;
1b1151
     crm_ipc_t *ipc;
1b1151
@@ -118,8 +85,6 @@ static int stonith_send_command(stonith_t *stonith, const char *op,
1b1151
 
1b1151
 static void stonith_connection_destroy(gpointer user_data);
1b1151
 static void stonith_send_notification(gpointer data, gpointer user_data);
1b1151
-static int internal_stonith_action_execute(stonith_action_t * action);
1b1151
-static void log_action(stonith_action_t *action, pid_t pid);
1b1151
 
1b1151
 /*!
1b1151
  * \brief Get agent namespace by name
1b1151
@@ -196,23 +161,6 @@ stonith_get_namespace(const char *agent, const char *namespace_s)
1b1151
     return st_namespace_invalid;
1b1151
 }
1b1151
 
1b1151
-/*!
1b1151
- * \internal
1b1151
- * \brief Set an action's result based on services library result
1b1151
- *
1b1151
- * \param[in] action      Fence action to set result for
1b1151
- * \param[in] svc_action  Service action to get result from
1b1151
- */
1b1151
-static void
1b1151
-set_result_from_svc_action(stonith_action_t *action, svc_action_t *svc_action)
1b1151
-{
1b1151
-    pcmk__set_result(&(action->result), svc_action->rc, svc_action->status,
1b1151
-                     services__exit_reason(svc_action));
1b1151
-    pcmk__set_result_output(&(action->result),
1b1151
-                            services__grab_stdout(svc_action),
1b1151
-                            services__grab_stderr(svc_action));
1b1151
-}
1b1151
-
1b1151
 gboolean
1b1151
 stonith__watchdog_fencing_enabled_for_node_api(stonith_t *st, const char *node)
1b1151
 {
1b1151
@@ -273,21 +221,6 @@ stonith__watchdog_fencing_enabled_for_node(const char *node)
1b1151
     return stonith__watchdog_fencing_enabled_for_node_api(NULL, node);
1b1151
 }
1b1151
 
1b1151
-static void
1b1151
-log_action(stonith_action_t *action, pid_t pid)
1b1151
-{
1b1151
-    /* The services library has already logged the output at info or debug
1b1151
-     * level, so just raise to warning for stderr.
1b1151
-     */
1b1151
-    if (action->result.action_stderr != NULL) {
1b1151
-        /* Logging the whole string confuses syslog when the string is xml */
1b1151
-        char *prefix = crm_strdup_printf("%s[%d] stderr:", action->agent, pid);
1b1151
-
1b1151
-        crm_log_output(LOG_WARNING, prefix, action->result.action_stderr);
1b1151
-        free(prefix);
1b1151
-    }
1b1151
-}
1b1151
-
1b1151
 /* when cycling through the list we don't want to delete items
1b1151
    so just mark them and when we know nobody is using the list
1b1151
    loop over it to remove the marked items
1b1151
@@ -530,465 +463,6 @@ stonith_api_register_level(stonith_t * st, int options, const char *node, int le
1b1151
                                            level, device_list);
1b1151
 }
1b1151
 
1b1151
-static void
1b1151
-append_config_arg(gpointer key, gpointer value, gpointer user_data)
1b1151
-{
1b1151
-    /* The fencer will filter "action" out when it registers the device,
1b1151
-     * but ignore it here in case any external API users don't.
1b1151
-     *
1b1151
-     * Also filter out parameters handled directly by Pacemaker.
1b1151
-     */
1b1151
-    if (!pcmk__str_eq(key, STONITH_ATTR_ACTION_OP, pcmk__str_casei)
1b1151
-        && !pcmk_stonith_param(key)
1b1151
-        && (strstr(key, CRM_META) == NULL)
1b1151
-        && !pcmk__str_eq(key, "crm_feature_set", pcmk__str_casei)) {
1b1151
-
1b1151
-        crm_trace("Passing %s=%s with fence action",
1b1151
-                  (const char *) key, (const char *) (value? value : ""));
1b1151
-        g_hash_table_insert((GHashTable *) user_data,
1b1151
-                            strdup(key), strdup(value? value : ""));
1b1151
-    }
1b1151
-}
1b1151
-
1b1151
-static GHashTable *
1b1151
-make_args(const char *agent, const char *action, const char *victim,
1b1151
-          uint32_t victim_nodeid, GHashTable * device_args,
1b1151
-          GHashTable * port_map, const char *host_arg)
1b1151
-{
1b1151
-    GHashTable *arg_list = NULL;
1b1151
-    const char *value = NULL;
1b1151
-
1b1151
-    CRM_CHECK(action != NULL, return NULL);
1b1151
-
1b1151
-    arg_list = pcmk__strkey_table(free, free);
1b1151
-
1b1151
-    // Add action to arguments (using an alias if requested)
1b1151
-    if (device_args) {
1b1151
-        char buffer[512];
1b1151
-
1b1151
-        snprintf(buffer, sizeof(buffer), "pcmk_%s_action", action);
1b1151
-        value = g_hash_table_lookup(device_args, buffer);
1b1151
-        if (value) {
1b1151
-            crm_debug("Substituting '%s' for fence action %s targeting %s",
1b1151
-                      value, action, victim);
1b1151
-            action = value;
1b1151
-        }
1b1151
-    }
1b1151
-    g_hash_table_insert(arg_list, strdup(STONITH_ATTR_ACTION_OP),
1b1151
-                        strdup(action));
1b1151
-
1b1151
-    /* If this is a fencing operation against another node, add more standard
1b1151
-     * arguments.
1b1151
-     */
1b1151
-    if (victim && device_args) {
1b1151
-        const char *param = NULL;
1b1151
-
1b1151
-        /* Always pass the target's name, per
1b1151
-         * https://github.com/ClusterLabs/fence-agents/blob/master/doc/FenceAgentAPI.md
1b1151
-         */
1b1151
-        g_hash_table_insert(arg_list, strdup("nodename"), strdup(victim));
1b1151
-
1b1151
-        // If the target's node ID was specified, pass it, too
1b1151
-        if (victim_nodeid) {
1b1151
-            char *nodeid = crm_strdup_printf("%" PRIu32, victim_nodeid);
1b1151
-
1b1151
-            // cts-fencing looks for this log message
1b1151
-            crm_info("Passing '%s' as nodeid with fence action '%s' targeting %s",
1b1151
-                     nodeid, action, victim);
1b1151
-            g_hash_table_insert(arg_list, strdup("nodeid"), nodeid);
1b1151
-        }
1b1151
-
1b1151
-        // Check whether target must be specified in some other way
1b1151
-        param = g_hash_table_lookup(device_args, PCMK_STONITH_HOST_ARGUMENT);
1b1151
-        if (!pcmk__str_eq(agent, "fence_legacy", pcmk__str_none)
1b1151
-            && !pcmk__str_eq(param, "none", pcmk__str_casei)) {
1b1151
-
1b1151
-            if (param == NULL) {
1b1151
-                /* Use the caller's default for pcmk_host_argument, or "port" if
1b1151
-                 * none was given
1b1151
-                 */
1b1151
-                param = (host_arg == NULL)? "port" : host_arg;
1b1151
-            }
1b1151
-            value = g_hash_table_lookup(device_args, param);
1b1151
-
1b1151
-            if (pcmk__str_eq(value, "dynamic",
1b1151
-                             pcmk__str_casei|pcmk__str_null_matches)) {
1b1151
-                /* If the host argument was "dynamic" or not explicitly specified,
1b1151
-                 * add it with the target
1b1151
-                 */
1b1151
-                const char *alias = NULL;
1b1151
-
1b1151
-                if (port_map) {
1b1151
-                    alias = g_hash_table_lookup(port_map, victim);
1b1151
-                }
1b1151
-                if (alias == NULL) {
1b1151
-                    alias = victim;
1b1151
-                }
1b1151
-                crm_debug("Passing %s='%s' with fence action %s targeting %s",
1b1151
-                          param, alias, action, victim);
1b1151
-                g_hash_table_insert(arg_list, strdup(param), strdup(alias));
1b1151
-            }
1b1151
-        }
1b1151
-    }
1b1151
-
1b1151
-    if (device_args) {
1b1151
-        g_hash_table_foreach(device_args, append_config_arg, arg_list);
1b1151
-    }
1b1151
-
1b1151
-    return arg_list;
1b1151
-}
1b1151
-
1b1151
-/*!
1b1151
- * \internal
1b1151
- * \brief Free all memory used by a stonith action
1b1151
- *
1b1151
- * \param[in,out] action  Action to free
1b1151
- */
1b1151
-void
1b1151
-stonith__destroy_action(stonith_action_t *action)
1b1151
-{
1b1151
-    if (action) {
1b1151
-        free(action->agent);
1b1151
-        if (action->args) {
1b1151
-            g_hash_table_destroy(action->args);
1b1151
-        }
1b1151
-        free(action->action);
1b1151
-        free(action->victim);
1b1151
-        if (action->svc_action) {
1b1151
-            services_action_free(action->svc_action);
1b1151
-        }
1b1151
-        pcmk__reset_result(&(action->result));
1b1151
-        free(action);
1b1151
-    }
1b1151
-}
1b1151
-
1b1151
-/*!
1b1151
- * \internal
1b1151
- * \brief Get the result of an executed stonith action
1b1151
- *
1b1151
- * \param[in] action  Executed action
1b1151
- *
1b1151
- * \return Pointer to action's result (or NULL if \p action is NULL)
1b1151
- */
1b1151
-pcmk__action_result_t *
1b1151
-stonith__action_result(stonith_action_t *action)
1b1151
-{
1b1151
-    return (action == NULL)? NULL : &(action->result);
1b1151
-}
1b1151
-
1b1151
-#define FAILURE_MAX_RETRIES 2
1b1151
-stonith_action_t *
1b1151
-stonith_action_create(const char *agent,
1b1151
-                      const char *_action,
1b1151
-                      const char *victim,
1b1151
-                      uint32_t victim_nodeid,
1b1151
-                      int timeout, GHashTable * device_args,
1b1151
-                      GHashTable * port_map, const char *host_arg)
1b1151
-{
1b1151
-    stonith_action_t *action;
1b1151
-
1b1151
-    action = calloc(1, sizeof(stonith_action_t));
1b1151
-    action->args = make_args(agent, _action, victim, victim_nodeid,
1b1151
-                             device_args, port_map, host_arg);
1b1151
-    crm_debug("Preparing '%s' action for %s using agent %s",
1b1151
-              _action, (victim? victim : "no target"), agent);
1b1151
-    action->agent = strdup(agent);
1b1151
-    action->action = strdup(_action);
1b1151
-    if (victim) {
1b1151
-        action->victim = strdup(victim);
1b1151
-    }
1b1151
-    action->timeout = action->remaining_timeout = timeout;
1b1151
-    action->max_retries = FAILURE_MAX_RETRIES;
1b1151
-
1b1151
-    pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN, PCMK_EXEC_UNKNOWN,
1b1151
-                     "Initialization bug in fencing library");
1b1151
-
1b1151
-    if (device_args) {
1b1151
-        char buffer[512];
1b1151
-        const char *value = NULL;
1b1151
-
1b1151
-        snprintf(buffer, sizeof(buffer), "pcmk_%s_retries", _action);
1b1151
-        value = g_hash_table_lookup(device_args, buffer);
1b1151
-
1b1151
-        if (value) {
1b1151
-            action->max_retries = atoi(value);
1b1151
-        }
1b1151
-    }
1b1151
-
1b1151
-    return action;
1b1151
-}
1b1151
-
1b1151
-static gboolean
1b1151
-update_remaining_timeout(stonith_action_t * action)
1b1151
-{
1b1151
-    int diff = time(NULL) - action->initial_start_time;
1b1151
-
1b1151
-    if (action->tries >= action->max_retries) {
1b1151
-        crm_info("Attempted to execute agent %s (%s) the maximum number of times (%d) allowed",
1b1151
-                 action->agent, action->action, action->max_retries);
1b1151
-        action->remaining_timeout = 0;
1b1151
-    } else if ((action->result.execution_status != PCMK_EXEC_TIMEOUT)
1b1151
-               && (diff < (action->timeout * 0.7))) {
1b1151
-        /* only set remaining timeout period if there is 30%
1b1151
-         * or greater of the original timeout period left */
1b1151
-        action->remaining_timeout = action->timeout - diff;
1b1151
-    } else {
1b1151
-        action->remaining_timeout = 0;
1b1151
-    }
1b1151
-    return action->remaining_timeout ? TRUE : FALSE;
1b1151
-}
1b1151
-
1b1151
-/*!
1b1151
- * \internal
1b1151
- * \brief Map a fencing action result to a standard return code
1b1151
- *
1b1151
- * \param[in] result  Fencing action result to map
1b1151
- *
1b1151
- * \return Standard Pacemaker return code that best corresponds to \p result
1b1151
- */
1b1151
-int
1b1151
-stonith__result2rc(const pcmk__action_result_t *result)
1b1151
-{
1b1151
-    if (pcmk__result_ok(result)) {
1b1151
-        return pcmk_rc_ok;
1b1151
-    }
1b1151
-
1b1151
-    switch (result->execution_status) {
1b1151
-        case PCMK_EXEC_PENDING:         return EINPROGRESS;
1b1151
-        case PCMK_EXEC_CANCELLED:       return ECANCELED;
1b1151
-        case PCMK_EXEC_TIMEOUT:         return ETIME;
1b1151
-        case PCMK_EXEC_NOT_INSTALLED:   return ENOENT;
1b1151
-        case PCMK_EXEC_NOT_SUPPORTED:   return EOPNOTSUPP;
1b1151
-        case PCMK_EXEC_NOT_CONNECTED:   return ENOTCONN;
1b1151
-        case PCMK_EXEC_NO_FENCE_DEVICE: return ENODEV;
1b1151
-        case PCMK_EXEC_NO_SECRETS:      return EACCES;
1b1151
-
1b1151
-        /* For the fencing API, PCMK_EXEC_INVALID is used with fencer API
1b1151
-         * operations that don't involve executing an agent (for example,
1b1151
-         * registering devices). This allows us to use the CRM_EX_* codes in the
1b1151
-         * exit status for finer-grained responses.
1b1151
-         */
1b1151
-        case PCMK_EXEC_INVALID:
1b1151
-            switch (result->exit_status) {
1b1151
-                case CRM_EX_INSUFFICIENT_PRIV:  return EACCES;
1b1151
-                case CRM_EX_PROTOCOL:           return EPROTO;
1b1151
-
1b1151
-               /* CRM_EX_EXPIRED is used for orphaned fencing operations left
1b1151
-                * over from a previous instance of the fencer. For API backward
1b1151
-                * compatibility, this is mapped to the previously used code for
1b1151
-                * this case, EHOSTUNREACH.
1b1151
-                */
1b1151
-                case CRM_EX_EXPIRED:            return EHOSTUNREACH;
1b1151
-                default:                        break;
1b1151
-            }
1b1151
-
1b1151
-        default:
1b1151
-            break;
1b1151
-    }
1b1151
-
1b1151
-    // Try to provide useful error code based on result's error output
1b1151
-
1b1151
-    if (result->action_stderr == NULL) {
1b1151
-        return ENODATA;
1b1151
-
1b1151
-    } else if (strcasestr(result->action_stderr, "timed out")
1b1151
-               || strcasestr(result->action_stderr, "timeout")) {
1b1151
-        return ETIME;
1b1151
-
1b1151
-    } else if (strcasestr(result->action_stderr, "unrecognised action")
1b1151
-               || strcasestr(result->action_stderr, "unrecognized action")
1b1151
-               || strcasestr(result->action_stderr, "unsupported action")) {
1b1151
-        return EOPNOTSUPP;
1b1151
-    }
1b1151
-
1b1151
-    // Oh well, we tried
1b1151
-    return pcmk_rc_error;
1b1151
-}
1b1151
-
1b1151
-static void
1b1151
-stonith_action_async_done(svc_action_t *svc_action)
1b1151
-{
1b1151
-    stonith_action_t *action = (stonith_action_t *) svc_action->cb_data;
1b1151
-
1b1151
-    set_result_from_svc_action(action, svc_action);
1b1151
-
1b1151
-    svc_action->params = NULL;
1b1151
-
1b1151
-    crm_debug("Child process %d performing action '%s' exited with rc %d",
1b1151
-                action->pid, action->action, svc_action->rc);
1b1151
-
1b1151
-    log_action(action, action->pid);
1b1151
-
1b1151
-    if (!pcmk__result_ok(&(action->result))
1b1151
-        && update_remaining_timeout(action)) {
1b1151
-
1b1151
-        int rc = internal_stonith_action_execute(action);
1b1151
-        if (rc == pcmk_ok) {
1b1151
-            return;
1b1151
-        }
1b1151
-    }
1b1151
-
1b1151
-    if (action->done_cb) {
1b1151
-        action->done_cb(action->pid, &(action->result), action->userdata);
1b1151
-    }
1b1151
-
1b1151
-    action->svc_action = NULL; // don't remove our caller
1b1151
-    stonith__destroy_action(action);
1b1151
-}
1b1151
-
1b1151
-static void
1b1151
-stonith_action_async_forked(svc_action_t *svc_action)
1b1151
-{
1b1151
-    stonith_action_t *action = (stonith_action_t *) svc_action->cb_data;
1b1151
-
1b1151
-    action->pid = svc_action->pid;
1b1151
-    action->svc_action = svc_action;
1b1151
-
1b1151
-    if (action->fork_cb) {
1b1151
-        (action->fork_cb) (svc_action->pid, action->userdata);
1b1151
-    }
1b1151
-
1b1151
-    crm_trace("Child process %d performing action '%s' successfully forked",
1b1151
-              action->pid, action->action);
1b1151
-}
1b1151
-
1b1151
-static int
1b1151
-internal_stonith_action_execute(stonith_action_t * action)
1b1151
-{
1b1151
-    int rc = -EPROTO;
1b1151
-    int is_retry = 0;
1b1151
-    svc_action_t *svc_action = NULL;
1b1151
-    static int stonith_sequence = 0;
1b1151
-    char *buffer = NULL;
1b1151
-
1b1151
-    CRM_CHECK(action != NULL, return -EINVAL);
1b1151
-
1b1151
-    if ((action->action == NULL) || (action->args == NULL)
1b1151
-        || (action->agent == NULL)) {
1b1151
-        pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN_ERROR,
1b1151
-                         PCMK_EXEC_ERROR_FATAL, "Bug in fencing library");
1b1151
-        return -EINVAL;
1b1151
-    }
1b1151
-
1b1151
-    if (!action->tries) {
1b1151
-        action->initial_start_time = time(NULL);
1b1151
-    }
1b1151
-    action->tries++;
1b1151
-
1b1151
-    if (action->tries > 1) {
1b1151
-        crm_info("Attempt %d to execute %s (%s). remaining timeout is %d",
1b1151
-                 action->tries, action->agent, action->action, action->remaining_timeout);
1b1151
-        is_retry = 1;
1b1151
-    }
1b1151
-
1b1151
-    buffer = crm_strdup_printf(PCMK__FENCE_BINDIR "/%s",
1b1151
-                               basename(action->agent));
1b1151
-    svc_action = services_action_create_generic(buffer, NULL);
1b1151
-    free(buffer);
1b1151
-
1b1151
-    if (svc_action->rc != PCMK_OCF_UNKNOWN) {
1b1151
-        set_result_from_svc_action(action, svc_action);
1b1151
-        services_action_free(svc_action);
1b1151
-        return -E2BIG;
1b1151
-    }
1b1151
-
1b1151
-    svc_action->timeout = 1000 * action->remaining_timeout;
1b1151
-    svc_action->standard = strdup(PCMK_RESOURCE_CLASS_STONITH);
1b1151
-    svc_action->id = crm_strdup_printf("%s_%s_%d", basename(action->agent),
1b1151
-                                       action->action, action->tries);
1b1151
-    svc_action->agent = strdup(action->agent);
1b1151
-    svc_action->sequence = stonith_sequence++;
1b1151
-    svc_action->params = action->args;
1b1151
-    svc_action->cb_data = (void *) action;
1b1151
-    svc_action->flags = pcmk__set_flags_as(__func__, __LINE__,
1b1151
-                                           LOG_TRACE, "Action",
1b1151
-                                           svc_action->id, svc_action->flags,
1b1151
-                                           SVC_ACTION_NON_BLOCKED,
1b1151
-                                           "SVC_ACTION_NON_BLOCKED");
1b1151
-
1b1151
-    /* keep retries from executing out of control and free previous results */
1b1151
-    if (is_retry) {
1b1151
-        pcmk__reset_result(&(action->result));
1b1151
-        sleep(1);
1b1151
-    }
1b1151
-
1b1151
-    if (action->async) {
1b1151
-        /* async */
1b1151
-        if (services_action_async_fork_notify(svc_action,
1b1151
-                                              &stonith_action_async_done,
1b1151
-                                              &stonith_action_async_forked)) {
1b1151
-            pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN,
1b1151
-                             PCMK_EXEC_PENDING, NULL);
1b1151
-            return pcmk_ok;
1b1151
-        }
1b1151
-
1b1151
-    } else if (services_action_sync(svc_action)) { // sync success
1b1151
-        rc = pcmk_ok;
1b1151
-
1b1151
-    } else { // sync failure
1b1151
-        rc = -ECONNABORTED;
1b1151
-    }
1b1151
-
1b1151
-    set_result_from_svc_action(action, svc_action);
1b1151
-    svc_action->params = NULL;
1b1151
-    services_action_free(svc_action);
1b1151
-    return rc;
1b1151
-}
1b1151
-
1b1151
-/*!
1b1151
- * \internal
1b1151
- * \brief Kick off execution of an async stonith action
1b1151
- *
1b1151
- * \param[in,out] action        Action to be executed
1b1151
- * \param[in,out] userdata      Datapointer to be passed to callbacks
1b1151
- * \param[in]     done          Callback to notify action has failed/succeeded
1b1151
- * \param[in]     fork_callback Callback to notify successful fork of child
1b1151
- *
1b1151
- * \return pcmk_ok if ownership of action has been taken, -errno otherwise
1b1151
- */
1b1151
-int
1b1151
-stonith_action_execute_async(stonith_action_t * action,
1b1151
-                             void *userdata,
1b1151
-                             void (*done) (int pid,
1b1151
-                                           const pcmk__action_result_t *result,
1b1151
-                                           void *user_data),
1b1151
-                             void (*fork_cb) (int pid, void *user_data))
1b1151
-{
1b1151
-    if (!action) {
1b1151
-        return -EINVAL;
1b1151
-    }
1b1151
-
1b1151
-    action->userdata = userdata;
1b1151
-    action->done_cb = done;
1b1151
-    action->fork_cb = fork_cb;
1b1151
-    action->async = 1;
1b1151
-
1b1151
-    return internal_stonith_action_execute(action);
1b1151
-}
1b1151
-
1b1151
-/*!
1b1151
- * \internal
1b1151
- * \brief Execute a stonith action
1b1151
- *
1b1151
- * \param[in,out] action  Action to execute
1b1151
- *
1b1151
- * \return pcmk_ok on success, -errno otherwise
1b1151
- */
1b1151
-int
1b1151
-stonith__execute(stonith_action_t *action)
1b1151
-{
1b1151
-    int rc = pcmk_ok;
1b1151
-
1b1151
-    CRM_CHECK(action != NULL, return -EINVAL);
1b1151
-
1b1151
-    // Keep trying until success, max retries, or timeout
1b1151
-    do {
1b1151
-        rc = internal_stonith_action_execute(action);
1b1151
-    } while ((rc != pcmk_ok) && update_remaining_timeout(action));
1b1151
-
1b1151
-    return rc;
1b1151
-}
1b1151
-
1b1151
 static int
1b1151
 stonith_api_device_list(stonith_t * stonith, int call_options, const char *namespace,
1b1151
                         stonith_key_value_t ** devices, int timeout)
1b1151
-- 
1b1151
2.27.0
1b1151
1b1151
1b1151
From 883a3cf7d3f73d02417d3997a7885dd5a7bebac7 Mon Sep 17 00:00:00 2001
1b1151
From: Ken Gaillot <kgaillot@redhat.com>
1b1151
Date: Wed, 10 Nov 2021 15:39:17 -0600
1b1151
Subject: [PATCH 05/13] Low: fencing,executor: improve mapping of legacy return
1b1151
 code to execution status
1b1151
1b1151
Move stonith_rc2status() from the executor to the fencing library for future
1b1151
reuse, exposing it internally as stonith__legacy2status(). Update it to use
1b1151
recently added execution status codes.
1b1151
---
1b1151
 daemons/execd/execd_commands.c | 66 ++++++++--------------------------
1b1151
 include/crm/fencing/internal.h |  2 ++
1b1151
 lib/fencing/st_actions.c       | 36 +++++++++++++++++++
1b1151
 3 files changed, 52 insertions(+), 52 deletions(-)
1b1151
1b1151
diff --git a/daemons/execd/execd_commands.c b/daemons/execd/execd_commands.c
1b1151
index 02070bf11..0ccaa1ced 100644
1b1151
--- a/daemons/execd/execd_commands.c
1b1151
+++ b/daemons/execd/execd_commands.c
1b1151
@@ -21,6 +21,7 @@
1b1151
 #include <unistd.h>
1b1151
 
1b1151
 #include <crm/crm.h>
1b1151
+#include <crm/fencing/internal.h>
1b1151
 #include <crm/services.h>
1b1151
 #include <crm/services_internal.h>
1b1151
 #include <crm/common/mainloop.h>
1b1151
@@ -999,56 +1000,6 @@ action_complete(svc_action_t * action)
1b1151
     cmd_finalize(cmd, rsc);
1b1151
 }
1b1151
 
1b1151
-/*!
1b1151
- * \internal
1b1151
- * \brief Determine operation status of a stonith operation
1b1151
- *
1b1151
- * Non-stonith resource operations get their operation status directly from the
1b1151
- * service library, but the fencer does not have an equivalent, so we must infer
1b1151
- * an operation status from the fencer API's return code.
1b1151
- *
1b1151
- * \param[in] action       Name of action performed on stonith resource
1b1151
- * \param[in] interval_ms  Action interval
1b1151
- * \param[in] rc           Action result from fencer
1b1151
- *
1b1151
- * \return Operation status corresponding to fencer API return code
1b1151
- */
1b1151
-static int
1b1151
-stonith_rc2status(const char *action, guint interval_ms, int rc)
1b1151
-{
1b1151
-    int status = PCMK_EXEC_DONE;
1b1151
-
1b1151
-    switch (rc) {
1b1151
-        case pcmk_ok:
1b1151
-            break;
1b1151
-
1b1151
-        case -EOPNOTSUPP:
1b1151
-        case -EPROTONOSUPPORT:
1b1151
-            status = PCMK_EXEC_NOT_SUPPORTED;
1b1151
-            break;
1b1151
-
1b1151
-        case -ETIME:
1b1151
-        case -ETIMEDOUT:
1b1151
-            status = PCMK_EXEC_TIMEOUT;
1b1151
-            break;
1b1151
-
1b1151
-        case -ENOTCONN:
1b1151
-        case -ECOMM:
1b1151
-            // Couldn't talk to fencer
1b1151
-            status = PCMK_EXEC_ERROR;
1b1151
-            break;
1b1151
-
1b1151
-        case -ENODEV:
1b1151
-            // The device is not registered with the fencer
1b1151
-            status = PCMK_EXEC_ERROR;
1b1151
-            break;
1b1151
-
1b1151
-        default:
1b1151
-            break;
1b1151
-    }
1b1151
-    return status;
1b1151
-}
1b1151
-
1b1151
 static void
1b1151
 stonith_action_complete(lrmd_cmd_t * cmd, int rc)
1b1151
 {
1b1151
@@ -1062,8 +1013,19 @@ stonith_action_complete(lrmd_cmd_t * cmd, int rc)
1b1151
      * the fencer return code.
1b1151
      */
1b1151
     if (cmd->result.execution_status != PCMK_EXEC_CANCELLED) {
1b1151
-        cmd->result.execution_status = stonith_rc2status(cmd->action,
1b1151
-                                                         cmd->interval_ms, rc);
1b1151
+        cmd->result.execution_status = stonith__legacy2status(rc);
1b1151
+
1b1151
+        // Simplify status codes from fencer
1b1151
+        switch (cmd->result.execution_status) {
1b1151
+            case PCMK_EXEC_NOT_CONNECTED:
1b1151
+            case PCMK_EXEC_INVALID:
1b1151
+            case PCMK_EXEC_NO_FENCE_DEVICE:
1b1151
+            case PCMK_EXEC_NO_SECRETS:
1b1151
+                cmd->result.execution_status = PCMK_EXEC_ERROR;
1b1151
+                break;
1b1151
+            default:
1b1151
+                break;
1b1151
+        }
1b1151
 
1b1151
         // Certain successful actions change the known state of the resource
1b1151
         if ((rsc != NULL) && pcmk__result_ok(&(cmd->result))) {
1b1151
diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h
1b1151
index 6a7e4232c..80f6443be 100644
1b1151
--- a/include/crm/fencing/internal.h
1b1151
+++ b/include/crm/fencing/internal.h
1b1151
@@ -182,6 +182,8 @@ bool stonith__event_state_pending(stonith_history_t *history, void *user_data);
1b1151
 bool stonith__event_state_eq(stonith_history_t *history, void *user_data);
1b1151
 bool stonith__event_state_neq(stonith_history_t *history, void *user_data);
1b1151
 
1b1151
+int stonith__legacy2status(int rc);
1b1151
+
1b1151
 /*!
1b1151
  * \internal
1b1151
  * \brief Is a fencing operation in pending state?
1b1151
diff --git a/lib/fencing/st_actions.c b/lib/fencing/st_actions.c
1b1151
index 64d3afd5d..9e785595a 100644
1b1151
--- a/lib/fencing/st_actions.c
1b1151
+++ b/lib/fencing/st_actions.c
1b1151
@@ -360,6 +360,42 @@ stonith__result2rc(const pcmk__action_result_t *result)
1b1151
     return pcmk_rc_error;
1b1151
 }
1b1151
 
1b1151
+/*!
1b1151
+ * \internal
1b1151
+ * \brief Determine execution status equivalent of legacy fencer return code
1b1151
+ *
1b1151
+ * Fence action notifications, and fence action callbacks from older fencers
1b1151
+ * (<=2.1.2) in a rolling upgrade, will have only a legacy return code. Map this
1b1151
+ * to an execution status as best as possible (essentially, the inverse of
1b1151
+ * stonith__result2rc()).
1b1151
+ *
1b1151
+ * \param[in] rc           Legacy return code from fencer
1b1151
+ *
1b1151
+ * \return Execution status best corresponding to \p rc
1b1151
+ */
1b1151
+int
1b1151
+stonith__legacy2status(int rc)
1b1151
+{
1b1151
+    if (rc >= 0) {
1b1151
+        return PCMK_EXEC_DONE;
1b1151
+    }
1b1151
+    switch (-rc) {
1b1151
+        case EACCES:            return PCMK_EXEC_NO_SECRETS;
1b1151
+        case ECANCELED:         return PCMK_EXEC_CANCELLED;
1b1151
+        case EHOSTUNREACH:      return PCMK_EXEC_INVALID;
1b1151
+        case EINPROGRESS:       return PCMK_EXEC_PENDING;
1b1151
+        case ENODEV:            return PCMK_EXEC_NO_FENCE_DEVICE;
1b1151
+        case ENOENT:            return PCMK_EXEC_NOT_INSTALLED;
1b1151
+        case ENOTCONN:          return PCMK_EXEC_NOT_CONNECTED;
1b1151
+        case EOPNOTSUPP:        return PCMK_EXEC_NOT_SUPPORTED;
1b1151
+        case EPROTO:            return PCMK_EXEC_INVALID;
1b1151
+        case EPROTONOSUPPORT:   return PCMK_EXEC_NOT_SUPPORTED;
1b1151
+        case ETIME:             return PCMK_EXEC_TIMEOUT;
1b1151
+        case ETIMEDOUT:         return PCMK_EXEC_TIMEOUT;
1b1151
+        default:                return PCMK_EXEC_ERROR;
1b1151
+    }
1b1151
+}
1b1151
+
1b1151
 static void
1b1151
 stonith_action_async_done(svc_action_t *svc_action)
1b1151
 {
1b1151
-- 
1b1151
2.27.0
1b1151
1b1151
1b1151
From 639a9f4a2cbeb6cc41b754a1dcb1f360a9500e03 Mon Sep 17 00:00:00 2001
1b1151
From: Ken Gaillot <kgaillot@redhat.com>
1b1151
Date: Thu, 11 Nov 2021 16:54:32 -0600
1b1151
Subject: [PATCH 06/13] Refactor: fencing: add functions for getting/setting
1b1151
 result via XML
1b1151
1b1151
These will come in handy as we update the various fencer messages to include a
1b1151
full result rather than just a legacy return code. The functions are in a new
1b1151
source file fenced_messages.c which can have other stuff moved to it later.
1b1151
---
1b1151
 include/crm/fencing/internal.h |   3 +
1b1151
 lib/fencing/st_actions.c       | 107 +++++++++++++++++++++++++++++++++
1b1151
 2 files changed, 110 insertions(+)
1b1151
1b1151
diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h
1b1151
index 80f6443be..4b5fd3959 100644
1b1151
--- a/include/crm/fencing/internal.h
1b1151
+++ b/include/crm/fencing/internal.h
1b1151
@@ -60,6 +60,9 @@ stonith_action_t *stonith_action_create(const char *agent,
1b1151
 void stonith__destroy_action(stonith_action_t *action);
1b1151
 pcmk__action_result_t *stonith__action_result(stonith_action_t *action);
1b1151
 int stonith__result2rc(const pcmk__action_result_t *result);
1b1151
+void stonith__xe_set_result(xmlNode *xml, const pcmk__action_result_t *result);
1b1151
+void stonith__xe_get_result(xmlNode *xml, pcmk__action_result_t *result);
1b1151
+xmlNode *stonith__find_xe_with_result(xmlNode *xml);
1b1151
 
1b1151
 int
1b1151
 stonith_action_execute_async(stonith_action_t * action,
1b1151
diff --git a/lib/fencing/st_actions.c b/lib/fencing/st_actions.c
1b1151
index 9e785595a..d4fc3f5ed 100644
1b1151
--- a/lib/fencing/st_actions.c
1b1151
+++ b/lib/fencing/st_actions.c
1b1151
@@ -396,6 +396,113 @@ stonith__legacy2status(int rc)
1b1151
     }
1b1151
 }
1b1151
 
1b1151
+/*!
1b1151
+ * \internal
1b1151
+ * \brief Add a fencing result to an XML element as attributes
1b1151
+ *
1b1151
+ * \param[in] xml     XML element to add result to
1b1151
+ * \param[in] result  Fencing result to add (assume success if NULL)
1b1151
+ */
1b1151
+void
1b1151
+stonith__xe_set_result(xmlNode *xml, const pcmk__action_result_t *result)
1b1151
+{
1b1151
+    int exit_status = CRM_EX_OK;
1b1151
+    enum pcmk_exec_status execution_status = PCMK_EXEC_DONE;
1b1151
+    const char *exit_reason = NULL;
1b1151
+    const char *action_stdout = NULL;
1b1151
+    int rc = pcmk_ok;
1b1151
+
1b1151
+    CRM_CHECK(xml != NULL, return);
1b1151
+
1b1151
+    if (result != NULL) {
1b1151
+        exit_status = result->exit_status;
1b1151
+        execution_status = result->execution_status;
1b1151
+        exit_reason = result->exit_reason;
1b1151
+        action_stdout = result->action_stdout;
1b1151
+        rc = pcmk_rc2legacy(stonith__result2rc(result));
1b1151
+    }
1b1151
+
1b1151
+    crm_xml_add_int(xml, XML_LRM_ATTR_OPSTATUS, (int) execution_status);
1b1151
+    crm_xml_add_int(xml, XML_LRM_ATTR_RC, exit_status);
1b1151
+    crm_xml_add(xml, XML_LRM_ATTR_EXIT_REASON, exit_reason);
1b1151
+    crm_xml_add(xml, "st_output", action_stdout);
1b1151
+
1b1151
+    /* @COMPAT Peers in rolling upgrades, Pacemaker Remote nodes, and external
1b1151
+     * code that use libstonithd <=2.1.2 don't check for the full result, and
1b1151
+     * need a legacy return code instead.
1b1151
+     */
1b1151
+    crm_xml_add_int(xml, F_STONITH_RC, rc);
1b1151
+}
1b1151
+
1b1151
+/*!
1b1151
+ * \internal
1b1151
+ * \brief Find a fencing result beneath an XML element
1b1151
+ *
1b1151
+ * \param[in]  xml     XML element to search
1b1151
+ *
1b1151
+ * \return \p xml or descendent of it that contains a fencing result, else NULL
1b1151
+ */
1b1151
+xmlNode *
1b1151
+stonith__find_xe_with_result(xmlNode *xml)
1b1151
+{
1b1151
+    xmlNode *match = get_xpath_object("//@" XML_LRM_ATTR_RC, xml, LOG_NEVER);
1b1151
+
1b1151
+    if (match == NULL) {
1b1151
+        /* @COMPAT Peers <=2.1.2 in a rolling upgrade provide only a legacy
1b1151
+         * return code, not a full result, so check for that.
1b1151
+         */
1b1151
+        match = get_xpath_object("//@" F_STONITH_RC, xml, LOG_ERR);
1b1151
+    }
1b1151
+    return match;
1b1151
+}
1b1151
+
1b1151
+/*!
1b1151
+ * \internal
1b1151
+ * \brief Get a fencing result from an XML element's attributes
1b1151
+ *
1b1151
+ * \param[in]  xml     XML element with fencing result
1b1151
+ * \param[out] result  Where to store fencing result
1b1151
+ */
1b1151
+void
1b1151
+stonith__xe_get_result(xmlNode *xml, pcmk__action_result_t *result)
1b1151
+{
1b1151
+    int exit_status = CRM_EX_OK;
1b1151
+    int execution_status = PCMK_EXEC_DONE;
1b1151
+    const char *exit_reason = NULL;
1b1151
+    char *action_stdout = NULL;
1b1151
+
1b1151
+    CRM_CHECK((xml != NULL) && (result != NULL), return);
1b1151
+
1b1151
+    exit_reason = crm_element_value(xml, XML_LRM_ATTR_EXIT_REASON);
1b1151
+    action_stdout = crm_element_value_copy(xml, "st_output");
1b1151
+
1b1151
+    // A result must include an exit status and execution status
1b1151
+    if ((crm_element_value_int(xml, XML_LRM_ATTR_RC, &exit_status) < 0)
1b1151
+        || (crm_element_value_int(xml, XML_LRM_ATTR_OPSTATUS,
1b1151
+                                  &execution_status) < 0)) {
1b1151
+        int rc = pcmk_ok;
1b1151
+        exit_status = CRM_EX_ERROR;
1b1151
+
1b1151
+        /* @COMPAT Peers <=2.1.2 in rolling upgrades provide only a legacy
1b1151
+         * return code, not a full result, so check for that.
1b1151
+         */
1b1151
+        if (crm_element_value_int(xml, F_STONITH_RC, &rc) == 0) {
1b1151
+            if ((rc == pcmk_ok) || (rc == -EINPROGRESS)) {
1b1151
+                exit_status = CRM_EX_OK;
1b1151
+            }
1b1151
+            execution_status = stonith__legacy2status(rc);
1b1151
+            exit_reason = pcmk_strerror(rc);
1b1151
+
1b1151
+        } else {
1b1151
+            execution_status = PCMK_EXEC_ERROR;
1b1151
+            exit_reason = "Fencer reply contained neither a full result "
1b1151
+                          "nor a legacy return code (bug?)";
1b1151
+        }
1b1151
+    }
1b1151
+    pcmk__set_result(result, exit_status, execution_status, exit_reason);
1b1151
+    pcmk__set_result_output(result, action_stdout, NULL);
1b1151
+}
1b1151
+
1b1151
 static void
1b1151
 stonith_action_async_done(svc_action_t *svc_action)
1b1151
 {
1b1151
-- 
1b1151
2.27.0
1b1151
1b1151
1b1151
From 1f0121c6ad0d0235bcf01c8b60f9153592b3db83 Mon Sep 17 00:00:00 2001
1b1151
From: Ken Gaillot <kgaillot@redhat.com>
1b1151
Date: Thu, 11 Nov 2021 10:10:53 -0600
1b1151
Subject: [PATCH 07/13] Refactor: fencing: rename functions for invoking fence
1b1151
 callbacks
1b1151
1b1151
... to make it clearer what the difference between them is
1b1151
---
1b1151
 lib/fencing/st_client.c | 44 +++++++++++++++++++++++++++++++++--------
1b1151
 1 file changed, 36 insertions(+), 8 deletions(-)
1b1151
1b1151
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
1b1151
index 944cd1863..dfc5860fc 100644
1b1151
--- a/lib/fencing/st_client.c
1b1151
+++ b/lib/fencing/st_client.c
1b1151
@@ -847,9 +847,21 @@ stonith_api_del_callback(stonith_t * stonith, int call_id, bool all_callbacks)
1b1151
     return pcmk_ok;
1b1151
 }
1b1151
 
1b1151
+/*!
1b1151
+ * \internal
1b1151
+ * \brief Invoke a (single) specified fence action callback
1b1151
+ *
1b1151
+ * \param[in] st        Fencer API connection
1b1151
+ * \param[in] call_id   If positive, call ID of completed fence action, otherwise
1b1151
+ *                      legacy return code for early action failure
1b1151
+ * \param[in] rc        Legacy return code for action result
1b1151
+ * \param[in] userdata  User data to pass to callback
1b1151
+ * \param[in] callback  Fence action callback to invoke
1b1151
+ */
1b1151
 static void
1b1151
-invoke_callback(stonith_t * st, int call_id, int rc, void *userdata,
1b1151
-                void (*callback) (stonith_t * st, stonith_callback_data_t * data))
1b1151
+invoke_fence_action_callback(stonith_t *st, int call_id, int rc, void *userdata,
1b1151
+                             void (*callback) (stonith_t *st,
1b1151
+                                               stonith_callback_data_t *data))
1b1151
 {
1b1151
     stonith_callback_data_t data = { 0, };
1b1151
 
1b1151
@@ -860,8 +872,21 @@ invoke_callback(stonith_t * st, int call_id, int rc, void *userdata,
1b1151
     callback(st, &data);
1b1151
 }
1b1151
 
1b1151
+/*!
1b1151
+ * \internal
1b1151
+ * \brief Invoke any callbacks registered for a specified fence action result
1b1151
+ *
1b1151
+ * Given a fence action result from the fencer, invoke any callback registered
1b1151
+ * for that action, as well as any global callback registered.
1b1151
+ *
1b1151
+ * \param[in] st        Fencer API connection
1b1151
+ * \param[in] msg       If non-NULL, fencer reply
1b1151
+ * \param[in] call_id   If \p msg is NULL, call ID of action that timed out
1b1151
+ * \param[in] rc        Legacy return code for result of action
1b1151
+ */
1b1151
 static void
1b1151
-stonith_perform_callback(stonith_t * stonith, xmlNode * msg, int call_id, int rc)
1b1151
+invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id,
1b1151
+                            int rc)
1b1151
 {
1b1151
     stonith_private_t *private = NULL;
1b1151
     stonith_callback_client_t *blob = NULL;
1b1151
@@ -899,7 +924,8 @@ stonith_perform_callback(stonith_t * stonith, xmlNode * msg, int call_id, int rc
1b1151
 
1b1151
     if (local_blob.callback != NULL && (rc == pcmk_ok || local_blob.only_success == FALSE)) {
1b1151
         crm_trace("Invoking callback %s for call %d", crm_str(local_blob.id), call_id);
1b1151
-        invoke_callback(stonith, call_id, rc, local_blob.user_data, local_blob.callback);
1b1151
+        invoke_fence_action_callback(stonith, call_id, rc, local_blob.user_data,
1b1151
+                                     local_blob.callback);
1b1151
 
1b1151
     } else if (private->op_callback == NULL && rc != pcmk_ok) {
1b1151
         crm_warn("Fencing command failed: %s", pcmk_strerror(rc));
1b1151
@@ -908,7 +934,8 @@ stonith_perform_callback(stonith_t * stonith, xmlNode * msg, int call_id, int rc
1b1151
 
1b1151
     if (private->op_callback != NULL) {
1b1151
         crm_trace("Invoking global callback for call %d", call_id);
1b1151
-        invoke_callback(stonith, call_id, rc, NULL, private->op_callback);
1b1151
+        invoke_fence_action_callback(stonith, call_id, rc, NULL,
1b1151
+                                     private->op_callback);
1b1151
     }
1b1151
     crm_trace("OP callback activated.");
1b1151
 }
1b1151
@@ -919,7 +946,7 @@ stonith_async_timeout_handler(gpointer data)
1b1151
     struct timer_rec_s *timer = data;
1b1151
 
1b1151
     crm_err("Async call %d timed out after %dms", timer->call_id, timer->timeout);
1b1151
-    stonith_perform_callback(timer->stonith, NULL, timer->call_id, -ETIME);
1b1151
+    invoke_registered_callbacks(timer->stonith, NULL, timer->call_id, -ETIME);
1b1151
 
1b1151
     /* Always return TRUE, never remove the handler
1b1151
      * We do that in stonith_del_callback()
1b1151
@@ -994,7 +1021,7 @@ stonith_dispatch_internal(const char *buffer, ssize_t length, gpointer userdata)
1b1151
     crm_trace("Activating %s callbacks...", type);
1b1151
 
1b1151
     if (pcmk__str_eq(type, T_STONITH_NG, pcmk__str_casei)) {
1b1151
-        stonith_perform_callback(st, blob.xml, 0, 0);
1b1151
+        invoke_registered_callbacks(st, blob.xml, 0, 0);
1b1151
 
1b1151
     } else if (pcmk__str_eq(type, T_STONITH_NOTIFY, pcmk__str_casei)) {
1b1151
         foreach_notify_entry(private, stonith_send_notification, &blob;;
1b1151
@@ -1229,7 +1256,8 @@ stonith_api_add_callback(stonith_t * stonith, int call_id, int timeout, int opti
1b1151
     } else if (call_id < 0) {
1b1151
         if (!(options & st_opt_report_only_success)) {
1b1151
             crm_trace("Call failed, calling %s: %s", callback_name, pcmk_strerror(call_id));
1b1151
-            invoke_callback(stonith, call_id, call_id, user_data, callback);
1b1151
+            invoke_fence_action_callback(stonith, call_id, call_id, user_data,
1b1151
+                                         callback);
1b1151
         } else {
1b1151
             crm_warn("Fencer call failed: %s", pcmk_strerror(call_id));
1b1151
         }
1b1151
-- 
1b1151
2.27.0
1b1151
1b1151
1b1151
From c32f11e70a88244f5a3217608055a4eaf8d28231 Mon Sep 17 00:00:00 2001
1b1151
From: Ken Gaillot <kgaillot@redhat.com>
1b1151
Date: Thu, 11 Nov 2021 10:21:00 -0600
1b1151
Subject: [PATCH 08/13] Refactor: fencing: drop unnecessary argument when
1b1151
 invoking callbacks
1b1151
1b1151
Refactor invoke_registered_callbacks() to treat a NULL message as a timeout, so
1b1151
we can drop the rc argument.
1b1151
---
1b1151
 lib/fencing/st_client.c | 17 +++++++++++------
1b1151
 1 file changed, 11 insertions(+), 6 deletions(-)
1b1151
1b1151
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
1b1151
index dfc5860fc..9f2b0c1c1 100644
1b1151
--- a/lib/fencing/st_client.c
1b1151
+++ b/lib/fencing/st_client.c
1b1151
@@ -882,15 +882,14 @@ invoke_fence_action_callback(stonith_t *st, int call_id, int rc, void *userdata,
1b1151
  * \param[in] st        Fencer API connection
1b1151
  * \param[in] msg       If non-NULL, fencer reply
1b1151
  * \param[in] call_id   If \p msg is NULL, call ID of action that timed out
1b1151
- * \param[in] rc        Legacy return code for result of action
1b1151
  */
1b1151
 static void
1b1151
-invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id,
1b1151
-                            int rc)
1b1151
+invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id)
1b1151
 {
1b1151
     stonith_private_t *private = NULL;
1b1151
     stonith_callback_client_t *blob = NULL;
1b1151
     stonith_callback_client_t local_blob;
1b1151
+    int rc = pcmk_ok;
1b1151
 
1b1151
     CRM_CHECK(stonith != NULL, return);
1b1151
     CRM_CHECK(stonith->st_private != NULL, return);
1b1151
@@ -902,7 +901,13 @@ invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id,
1b1151
     local_blob.user_data = NULL;
1b1151
     local_blob.only_success = FALSE;
1b1151
 
1b1151
-    if (msg != NULL) {
1b1151
+    if (msg == NULL) {
1b1151
+        // Fencer didn't reply in time
1b1151
+        rc = -ETIME;
1b1151
+
1b1151
+    } else {
1b1151
+        // We have the fencer reply
1b1151
+
1b1151
         crm_element_value_int(msg, F_STONITH_RC, &rc);
1b1151
         crm_element_value_int(msg, F_STONITH_CALLID, &call_id);
1b1151
     }
1b1151
@@ -946,7 +951,7 @@ stonith_async_timeout_handler(gpointer data)
1b1151
     struct timer_rec_s *timer = data;
1b1151
 
1b1151
     crm_err("Async call %d timed out after %dms", timer->call_id, timer->timeout);
1b1151
-    invoke_registered_callbacks(timer->stonith, NULL, timer->call_id, -ETIME);
1b1151
+    invoke_registered_callbacks(timer->stonith, NULL, timer->call_id);
1b1151
 
1b1151
     /* Always return TRUE, never remove the handler
1b1151
      * We do that in stonith_del_callback()
1b1151
@@ -1021,7 +1026,7 @@ stonith_dispatch_internal(const char *buffer, ssize_t length, gpointer userdata)
1b1151
     crm_trace("Activating %s callbacks...", type);
1b1151
 
1b1151
     if (pcmk__str_eq(type, T_STONITH_NG, pcmk__str_casei)) {
1b1151
-        invoke_registered_callbacks(st, blob.xml, 0, 0);
1b1151
+        invoke_registered_callbacks(st, blob.xml, 0);
1b1151
 
1b1151
     } else if (pcmk__str_eq(type, T_STONITH_NOTIFY, pcmk__str_casei)) {
1b1151
         foreach_notify_entry(private, stonith_send_notification, &blob;;
1b1151
-- 
1b1151
2.27.0
1b1151
1b1151
1b1151
From 5d8279b51ea9df738354649e4065663f2c16f1e6 Mon Sep 17 00:00:00 2001
1b1151
From: Ken Gaillot <kgaillot@redhat.com>
1b1151
Date: Thu, 11 Nov 2021 10:21:57 -0600
1b1151
Subject: [PATCH 09/13] Log: fencing: improve message for callback errors
1b1151
1b1151
Improve checking of fencer replies, which also allows us to distinguish an
1b1151
internal bug from a bad fencer reply in logs. Lower the bad reply message to
1b1151
warning.
1b1151
---
1b1151
 lib/fencing/st_client.c | 13 +++++++++----
1b1151
 1 file changed, 9 insertions(+), 4 deletions(-)
1b1151
1b1151
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
1b1151
index 9f2b0c1c1..170b9d450 100644
1b1151
--- a/lib/fencing/st_client.c
1b1151
+++ b/lib/fencing/st_client.c
1b1151
@@ -904,15 +904,20 @@ invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id)
1b1151
     if (msg == NULL) {
1b1151
         // Fencer didn't reply in time
1b1151
         rc = -ETIME;
1b1151
+        CRM_LOG_ASSERT(call_id > 0);
1b1151
 
1b1151
     } else {
1b1151
         // We have the fencer reply
1b1151
 
1b1151
-        crm_element_value_int(msg, F_STONITH_RC, &rc);
1b1151
-        crm_element_value_int(msg, F_STONITH_CALLID, &call_id);
1b1151
-    }
1b1151
+        if (crm_element_value_int(msg, F_STONITH_RC, &rc) != 0) {
1b1151
+            rc = -pcmk_err_generic;
1b1151
+        }
1b1151
 
1b1151
-    CRM_CHECK(call_id > 0, crm_log_xml_err(msg, "Bad result"));
1b1151
+        if ((crm_element_value_int(msg, F_STONITH_CALLID, &call_id) != 0)
1b1151
+            || (call_id <= 0)) {
1b1151
+            crm_log_xml_warn(msg, "Bad fencer reply");
1b1151
+        }
1b1151
+    }
1b1151
 
1b1151
     blob = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
1b1151
                                      call_id);
1b1151
-- 
1b1151
2.27.0
1b1151
1b1151
1b1151
From e03c14d24e8cb011e870b9460930d139705bf0a2 Mon Sep 17 00:00:00 2001
1b1151
From: Ken Gaillot <kgaillot@redhat.com>
1b1151
Date: Tue, 9 Nov 2021 14:59:12 -0600
1b1151
Subject: [PATCH 10/13] Doc: fencing: correct stonith_api_operations_t method
1b1151
 descriptions
1b1151
1b1151
Many of the methods return a positive call ID on success
1b1151
---
1b1151
 include/crm/stonith-ng.h | 60 ++++++++++++++++++++++------------------
1b1151
 1 file changed, 33 insertions(+), 27 deletions(-)
1b1151
1b1151
diff --git a/include/crm/stonith-ng.h b/include/crm/stonith-ng.h
1b1151
index 8d6ad477d..9643820e9 100644
1b1151
--- a/include/crm/stonith-ng.h
1b1151
+++ b/include/crm/stonith-ng.h
1b1151
@@ -164,39 +164,38 @@ typedef struct stonith_api_operations_s
1b1151
     int (*disconnect)(stonith_t *st);
1b1151
 
1b1151
     /*!
1b1151
-     * \brief Remove a registered stonith device with the local stonith daemon.
1b1151
+     * \brief Unregister a fence device with the local fencer
1b1151
      *
1b1151
-     * \note Synchronous, guaranteed to occur in daemon before function returns.
1b1151
-     *
1b1151
-     * \return Legacy Pacemaker return code
1b1151
+     * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
1b1151
+     *         on success, otherwise a negative legacy Pacemaker return code
1b1151
      */
1b1151
     int (*remove_device)(
1b1151
         stonith_t *st, int options, const char *name);
1b1151
 
1b1151
     /*!
1b1151
-     * \brief Register a stonith device with the local stonith daemon.
1b1151
+     * \brief Register a fence device with the local fencer
1b1151
      *
1b1151
-     * \note Synchronous, guaranteed to occur in daemon before function returns.
1b1151
-     *
1b1151
-     * \return Legacy Pacemaker return code
1b1151
+     * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
1b1151
+     *         on success, otherwise a negative legacy Pacemaker return code
1b1151
      */
1b1151
     int (*register_device)(
1b1151
         stonith_t *st, int options, const char *id,
1b1151
         const char *provider, const char *agent, stonith_key_value_t *params);
1b1151
 
1b1151
     /*!
1b1151
-     * \brief Remove a fencing level for a specific node.
1b1151
+     * \brief Unregister a fencing level for specified node with local fencer
1b1151
      *
1b1151
-     * \return Legacy Pacemaker return code
1b1151
+     * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
1b1151
+     *         on success, otherwise a negative legacy Pacemaker return code
1b1151
      */
1b1151
     int (*remove_level)(
1b1151
         stonith_t *st, int options, const char *node, int level);
1b1151
 
1b1151
     /*!
1b1151
-     * \brief Register a fencing level containing the fencing devices to be used
1b1151
-     *        at that level for a specific node.
1b1151
+     * \brief Register a fencing level for specified node with local fencer
1b1151
      *
1b1151
-     * \return Legacy Pacemaker return code
1b1151
+     * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
1b1151
+     *         on success, otherwise a negative legacy Pacemaker return code
1b1151
      */
1b1151
     int (*register_level)(
1b1151
         stonith_t *st, int options, const char *node, int level, stonith_key_value_t *device_list);
1b1151
@@ -226,21 +225,24 @@ typedef struct stonith_api_operations_s
1b1151
     /*!
1b1151
      * \brief Retrieve string listing hosts and port assignments from a local stonith device.
1b1151
      *
1b1151
-     * \return Legacy Pacemaker return code
1b1151
+     * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
1b1151
+     *         on success, otherwise a negative legacy Pacemaker return code
1b1151
      */
1b1151
     int (*list)(stonith_t *st, int options, const char *id, char **list_output, int timeout);
1b1151
 
1b1151
     /*!
1b1151
      * \brief Check to see if a local stonith device is reachable
1b1151
      *
1b1151
-     * \return Legacy Pacemaker return code
1b1151
+     * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
1b1151
+     *         on success, otherwise a negative legacy Pacemaker return code
1b1151
      */
1b1151
     int (*monitor)(stonith_t *st, int options, const char *id, int timeout);
1b1151
 
1b1151
     /*!
1b1151
      * \brief Check to see if a local stonith device's port is reachable
1b1151
      *
1b1151
-     * \return Legacy Pacemaker return code
1b1151
+     * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
1b1151
+     *         on success, otherwise a negative legacy Pacemaker return code
1b1151
      */
1b1151
     int (*status)(stonith_t *st, int options, const char *id, const char *port, int timeout);
1b1151
 
1b1151
@@ -267,7 +269,8 @@ typedef struct stonith_api_operations_s
1b1151
      * \param timeout, The default per device timeout to use with each device
1b1151
      *                 capable of fencing the target.
1b1151
      *
1b1151
-     * \return Legacy Pacemaker return code
1b1151
+     * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
1b1151
+     *         on success, otherwise a negative legacy Pacemaker return code
1b1151
      */
1b1151
     int (*fence)(stonith_t *st, int options, const char *node, const char *action,
1b1151
                  int timeout, int tolerance);
1b1151
@@ -275,7 +278,8 @@ typedef struct stonith_api_operations_s
1b1151
     /*!
1b1151
      * \brief Manually confirm that a node is down.
1b1151
      *
1b1151
-     * \return Legacy Pacemaker return code
1b1151
+     * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
1b1151
+     *         on success, otherwise a negative legacy Pacemaker return code
1b1151
      */
1b1151
     int (*confirm)(stonith_t *st, int options, const char *node);
1b1151
 
1b1151
@@ -304,9 +308,6 @@ typedef struct stonith_api_operations_s
1b1151
      * \param[in] callback       The callback function to register
1b1151
      *
1b1151
      * \return \c TRUE on success, \c FALSE if call_id is negative, -errno otherwise
1b1151
-     *
1b1151
-     * \todo This function should return \c pcmk_ok on success, and \c call_id
1b1151
-     *       when negative, but that would break backward compatibility.
1b1151
      */
1b1151
     int (*register_callback)(stonith_t *st,
1b1151
         int call_id,
1b1151
@@ -317,12 +318,14 @@ typedef struct stonith_api_operations_s
1b1151
         void (*callback)(stonith_t *st, stonith_callback_data_t *data));
1b1151
 
1b1151
     /*!
1b1151
-     * \brief Remove a registered callback for a given call id.
1b1151
+     * \brief Remove a registered callback for a given call id
1b1151
+     *
1b1151
+     * \return pcmk_ok
1b1151
      */
1b1151
     int (*remove_callback)(stonith_t *st, int call_id, bool all_callbacks);
1b1151
 
1b1151
     /*!
1b1151
-     * \brief Remove fencing level for specific node, node regex or attribute
1b1151
+     * \brief Unregister fencing level for specified node, pattern or attribute
1b1151
      *
1b1151
      * \param[in] st      Fencer connection to use
1b1151
      * \param[in] options Bitmask of stonith_call_options to pass to the fencer
1b1151
@@ -332,7 +335,8 @@ typedef struct stonith_api_operations_s
1b1151
      * \param[in] value   If not NULL, target by this node attribute value
1b1151
      * \param[in] level   Index number of level to remove
1b1151
      *
1b1151
-     * \return 0 on success, negative error code otherwise
1b1151
+     * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
1b1151
+     *         on success, otherwise a negative legacy Pacemaker return code
1b1151
      *
1b1151
      * \note The caller should set only one of node, pattern or attr/value.
1b1151
      */
1b1151
@@ -341,7 +345,7 @@ typedef struct stonith_api_operations_s
1b1151
                              const char *attr, const char *value, int level);
1b1151
 
1b1151
     /*!
1b1151
-     * \brief Register fencing level for specific node, node regex or attribute
1b1151
+     * \brief Register fencing level for specified node, pattern or attribute
1b1151
      *
1b1151
      * \param[in] st          Fencer connection to use
1b1151
      * \param[in] options     Bitmask of stonith_call_options to pass to fencer
1b1151
@@ -352,7 +356,8 @@ typedef struct stonith_api_operations_s
1b1151
      * \param[in] level       Index number of level to add
1b1151
      * \param[in] device_list Devices to use in level
1b1151
      *
1b1151
-     * \return 0 on success, negative error code otherwise
1b1151
+     * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
1b1151
+     *         on success, otherwise a negative legacy Pacemaker return code
1b1151
      *
1b1151
      * \note The caller should set only one of node, pattern or attr/value.
1b1151
      */
1b1151
@@ -398,7 +403,8 @@ typedef struct stonith_api_operations_s
1b1151
      * \param delay, Apply a fencing delay. Value -1 means disable also any
1b1151
      *               static/random fencing delays from pcmk_delay_base/max
1b1151
      *
1b1151
-     * \return Legacy Pacemaker return code
1b1151
+     * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
1b1151
+     *         on success, otherwise a negative legacy Pacemaker return code
1b1151
      */
1b1151
     int (*fence_with_delay)(stonith_t *st, int options, const char *node, const char *action,
1b1151
                             int timeout, int tolerance, int delay);
1b1151
-- 
1b1151
2.27.0
1b1151
1b1151
1b1151
From 18c382731889b626b21ba6a14f9213ef1e45a524 Mon Sep 17 00:00:00 2001
1b1151
From: Ken Gaillot <kgaillot@redhat.com>
1b1151
Date: Tue, 23 Nov 2021 11:14:24 -0600
1b1151
Subject: [PATCH 11/13] Refactor: fencing: define constant for XML attribute
1b1151
 for action output
1b1151
1b1151
---
1b1151
 daemons/fenced/fenced_commands.c | 4 ++--
1b1151
 include/crm/fencing/internal.h   | 1 +
1b1151
 lib/fencing/st_actions.c         | 4 ++--
1b1151
 lib/fencing/st_client.c          | 2 +-
1b1151
 4 files changed, 6 insertions(+), 5 deletions(-)
1b1151
1b1151
diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c
1b1151
index 26501a4b3..aa14c52af 100644
1b1151
--- a/daemons/fenced/fenced_commands.c
1b1151
+++ b/daemons/fenced/fenced_commands.c
1b1151
@@ -2677,7 +2677,7 @@ stonith_construct_reply(xmlNode * request, const char *output, xmlNode * data, i
1b1151
 
1b1151
     crm_xml_add(reply, "st_origin", __func__);
1b1151
     crm_xml_add(reply, F_TYPE, T_STONITH_NG);
1b1151
-    crm_xml_add(reply, "st_output", output);
1b1151
+    crm_xml_add(reply, F_STONITH_OUTPUT, output);
1b1151
     crm_xml_add_int(reply, F_STONITH_RC, rc);
1b1151
 
1b1151
     if (request == NULL) {
1b1151
@@ -2743,7 +2743,7 @@ construct_async_reply(async_command_t *cmd, const pcmk__action_result_t *result)
1b1151
     crm_xml_add_int(reply, F_STONITH_CALLOPTS, cmd->options);
1b1151
     crm_xml_add_int(reply, F_STONITH_RC,
1b1151
                     pcmk_rc2legacy(stonith__result2rc(result)));
1b1151
-    crm_xml_add(reply, "st_output", result->action_stdout);
1b1151
+    crm_xml_add(reply, F_STONITH_OUTPUT, result->action_stdout);
1b1151
     return reply;
1b1151
 }
1b1151
 
1b1151
diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h
1b1151
index 4b5fd3959..f0d294a0b 100644
1b1151
--- a/include/crm/fencing/internal.h
1b1151
+++ b/include/crm/fencing/internal.h
1b1151
@@ -105,6 +105,7 @@ void stonith__device_parameter_flags(uint32_t *device_flags,
1b1151
 #  define F_STONITH_REMOTE_OP_ID  "st_remote_op"
1b1151
 #  define F_STONITH_REMOTE_OP_ID_RELAY  "st_remote_op_relay"
1b1151
 #  define F_STONITH_RC            "st_rc"
1b1151
+#  define F_STONITH_OUTPUT        "st_output"
1b1151
 /*! Timeout period per a device execution */
1b1151
 #  define F_STONITH_TIMEOUT       "st_timeout"
1b1151
 #  define F_STONITH_TOLERANCE     "st_tolerance"
1b1151
diff --git a/lib/fencing/st_actions.c b/lib/fencing/st_actions.c
1b1151
index d4fc3f5ed..5636810a5 100644
1b1151
--- a/lib/fencing/st_actions.c
1b1151
+++ b/lib/fencing/st_actions.c
1b1151
@@ -425,7 +425,7 @@ stonith__xe_set_result(xmlNode *xml, const pcmk__action_result_t *result)
1b1151
     crm_xml_add_int(xml, XML_LRM_ATTR_OPSTATUS, (int) execution_status);
1b1151
     crm_xml_add_int(xml, XML_LRM_ATTR_RC, exit_status);
1b1151
     crm_xml_add(xml, XML_LRM_ATTR_EXIT_REASON, exit_reason);
1b1151
-    crm_xml_add(xml, "st_output", action_stdout);
1b1151
+    crm_xml_add(xml, F_STONITH_OUTPUT, action_stdout);
1b1151
 
1b1151
     /* @COMPAT Peers in rolling upgrades, Pacemaker Remote nodes, and external
1b1151
      * code that use libstonithd <=2.1.2 don't check for the full result, and
1b1151
@@ -474,7 +474,7 @@ stonith__xe_get_result(xmlNode *xml, pcmk__action_result_t *result)
1b1151
     CRM_CHECK((xml != NULL) && (result != NULL), return);
1b1151
 
1b1151
     exit_reason = crm_element_value(xml, XML_LRM_ATTR_EXIT_REASON);
1b1151
-    action_stdout = crm_element_value_copy(xml, "st_output");
1b1151
+    action_stdout = crm_element_value_copy(xml, F_STONITH_OUTPUT);
1b1151
 
1b1151
     // A result must include an exit status and execution status
1b1151
     if ((crm_element_value_int(xml, XML_LRM_ATTR_RC, &exit_status) < 0)
1b1151
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
1b1151
index 170b9d450..2dfadf922 100644
1b1151
--- a/lib/fencing/st_client.c
1b1151
+++ b/lib/fencing/st_client.c
1b1151
@@ -600,7 +600,7 @@ stonith_api_list(stonith_t * stonith, int call_options, const char *id, char **l
1b1151
     if (output && list_info) {
1b1151
         const char *list_str;
1b1151
 
1b1151
-        list_str = crm_element_value(output, "st_output");
1b1151
+        list_str = crm_element_value(output, F_STONITH_OUTPUT);
1b1151
 
1b1151
         if (list_str) {
1b1151
             *list_info = strdup(list_str);
1b1151
-- 
1b1151
2.27.0
1b1151
1b1151
1b1151
From 9fe9ed5d46c810cb9c12eb07271373ab92d271cd Mon Sep 17 00:00:00 2001
1b1151
From: Ken Gaillot <kgaillot@redhat.com>
1b1151
Date: Tue, 23 Nov 2021 11:39:32 -0600
1b1151
Subject: [PATCH 12/13] Refactor: fencing: simplify invoking callbacks
1b1151
1b1151
---
1b1151
 lib/fencing/st_client.c | 42 +++++++++++++++++------------------------
1b1151
 1 file changed, 17 insertions(+), 25 deletions(-)
1b1151
1b1151
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
1b1151
index 2dfadf922..2ca094566 100644
1b1151
--- a/lib/fencing/st_client.c
1b1151
+++ b/lib/fencing/st_client.c
1b1151
@@ -887,8 +887,7 @@ static void
1b1151
 invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id)
1b1151
 {
1b1151
     stonith_private_t *private = NULL;
1b1151
-    stonith_callback_client_t *blob = NULL;
1b1151
-    stonith_callback_client_t local_blob;
1b1151
+    stonith_callback_client_t *cb_info = NULL;
1b1151
     int rc = pcmk_ok;
1b1151
 
1b1151
     CRM_CHECK(stonith != NULL, return);
1b1151
@@ -896,11 +895,6 @@ invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id)
1b1151
 
1b1151
     private = stonith->st_private;
1b1151
 
1b1151
-    local_blob.id = NULL;
1b1151
-    local_blob.callback = NULL;
1b1151
-    local_blob.user_data = NULL;
1b1151
-    local_blob.only_success = FALSE;
1b1151
-
1b1151
     if (msg == NULL) {
1b1151
         // Fencer didn't reply in time
1b1151
         rc = -ETIME;
1b1151
@@ -919,26 +913,21 @@ invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id)
1b1151
         }
1b1151
     }
1b1151
 
1b1151
-    blob = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
1b1151
-                                     call_id);
1b1151
-    if (blob != NULL) {
1b1151
-        local_blob = *blob;
1b1151
-        blob = NULL;
1b1151
-
1b1151
-        stonith_api_del_callback(stonith, call_id, FALSE);
1b1151
-
1b1151
-    } else {
1b1151
-        crm_trace("No callback found for call %d", call_id);
1b1151
-        local_blob.callback = NULL;
1b1151
+    if (call_id > 0) {
1b1151
+        cb_info = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
1b1151
+                                            call_id);
1b1151
     }
1b1151
 
1b1151
-    if (local_blob.callback != NULL && (rc == pcmk_ok || local_blob.only_success == FALSE)) {
1b1151
-        crm_trace("Invoking callback %s for call %d", crm_str(local_blob.id), call_id);
1b1151
-        invoke_fence_action_callback(stonith, call_id, rc, local_blob.user_data,
1b1151
-                                     local_blob.callback);
1b1151
+    if ((cb_info != NULL) && (cb_info->callback != NULL)
1b1151
+        && (rc == pcmk_ok || !(cb_info->only_success))) {
1b1151
+        crm_trace("Invoking callback %s for call %d",
1b1151
+                  crm_str(cb_info->id), call_id);
1b1151
+        invoke_fence_action_callback(stonith, call_id, rc, cb_info->user_data,
1b1151
+                                     cb_info->callback);
1b1151
 
1b1151
-    } else if (private->op_callback == NULL && rc != pcmk_ok) {
1b1151
-        crm_warn("Fencing command failed: %s", pcmk_strerror(rc));
1b1151
+    } else if ((private->op_callback == NULL) && (rc != pcmk_ok)) {
1b1151
+        crm_warn("Fencing action without registered callback failed: %s",
1b1151
+                 pcmk_strerror(rc));
1b1151
         crm_log_xml_debug(msg, "Failed fence update");
1b1151
     }
1b1151
 
1b1151
@@ -947,7 +936,10 @@ invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id)
1b1151
         invoke_fence_action_callback(stonith, call_id, rc, NULL,
1b1151
                                      private->op_callback);
1b1151
     }
1b1151
-    crm_trace("OP callback activated.");
1b1151
+
1b1151
+    if (cb_info != NULL) {
1b1151
+        stonith_api_del_callback(stonith, call_id, FALSE);
1b1151
+    }
1b1151
 }
1b1151
 
1b1151
 static gboolean
1b1151
-- 
1b1151
2.27.0
1b1151
1b1151
1b1151
From 8113b800ce677ba17a16ca176e8f6f9b4a042316 Mon Sep 17 00:00:00 2001
1b1151
From: Ken Gaillot <kgaillot@redhat.com>
1b1151
Date: Tue, 23 Nov 2021 18:14:48 -0600
1b1151
Subject: [PATCH 13/13] Refactor: fencing: add a missing "break" statement
1b1151
1b1151
No effect, but more correct
1b1151
---
1b1151
 lib/fencing/st_actions.c | 1 +
1b1151
 1 file changed, 1 insertion(+)
1b1151
1b1151
diff --git a/lib/fencing/st_actions.c b/lib/fencing/st_actions.c
1b1151
index 5636810a5..7eaa8b0f2 100644
1b1151
--- a/lib/fencing/st_actions.c
1b1151
+++ b/lib/fencing/st_actions.c
1b1151
@@ -336,6 +336,7 @@ stonith__result2rc(const pcmk__action_result_t *result)
1b1151
                 case CRM_EX_EXPIRED:            return EHOSTUNREACH;
1b1151
                 default:                        break;
1b1151
             }
1b1151
+            break;
1b1151
 
1b1151
         default:
1b1151
             break;
1b1151
-- 
1b1151
2.27.0
1b1151