root/lib/fencing/st_actions.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. set_result_from_svc_action
  2. log_action
  3. append_config_arg
  4. make_args
  5. stonith__destroy_action
  6. stonith__action_result
  7. stonith__action_create
  8. update_remaining_timeout
  9. stonith__result2rc
  10. stonith__legacy2status
  11. stonith__xe_set_result
  12. stonith__find_xe_with_result
  13. stonith__xe_get_result
  14. stonith_action_async_done
  15. stonith_action_async_forked
  16. internal_stonith_action_execute
  17. stonith__execute_async
  18. stonith__execute

   1 /*
   2  * Copyright 2004-2024 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdbool.h>
  13 #include <stdlib.h>
  14 #include <stdio.h>
  15 #include <string.h>
  16 #include <libgen.h>
  17 #include <inttypes.h>
  18 #include <sys/types.h>
  19 #include <glib.h>
  20 
  21 #include <crm/crm.h>
  22 #include <crm/stonith-ng.h>
  23 #include <crm/fencing/internal.h>
  24 #include <crm/common/xml.h>
  25 #include <crm/services_internal.h>
  26 
  27 #include "fencing_private.h"
  28 
  29 struct stonith_action_s {
  30     /*! user defined data */
  31     char *agent;
  32     char *action;
  33     GHashTable *args;
  34     int timeout;
  35     bool async;
  36     void *userdata;
  37     void (*done_cb) (int pid, const pcmk__action_result_t *result,
  38                      void *user_data);
  39     void (*fork_cb) (int pid, void *user_data);
  40 
  41     svc_action_t *svc_action;
  42 
  43     /*! internal timing information */
  44     time_t initial_start_time;
  45     int tries;
  46     int remaining_timeout;
  47     int max_retries;
  48 
  49     int pid;
  50     pcmk__action_result_t result;
  51 };
  52 
  53 static int internal_stonith_action_execute(stonith_action_t *action);
  54 static void log_action(stonith_action_t *action, pid_t pid);
  55 
  56 /*!
  57  * \internal
  58  * \brief Set an action's result based on services library result
  59  *
  60  * \param[in,out] action      Fence action to set result for
  61  * \param[in,out] svc_action  Service action to get result from
  62  */
  63 static void
  64 set_result_from_svc_action(stonith_action_t *action, svc_action_t *svc_action)
     /* [previous][next][first][last][top][bottom][index][help] */
  65 {
  66     pcmk__set_result(&(action->result), svc_action->rc, svc_action->status,
  67                      services__exit_reason(svc_action));
  68     pcmk__set_result_output(&(action->result),
  69                             services__grab_stdout(svc_action),
  70                             services__grab_stderr(svc_action));
  71 }
  72 
  73 static void
  74 log_action(stonith_action_t *action, pid_t pid)
     /* [previous][next][first][last][top][bottom][index][help] */
  75 {
  76     /* The services library has already logged the output at info or debug
  77      * level, so just raise to warning for stderr.
  78      */
  79     if (action->result.action_stderr != NULL) {
  80         /* Logging the whole string confuses syslog when the string is xml */
  81         char *prefix = crm_strdup_printf("%s[%d] stderr:", action->agent, pid);
  82 
  83         crm_log_output(LOG_WARNING, prefix, action->result.action_stderr);
  84         free(prefix);
  85     }
  86 }
  87 
  88 static void
  89 append_config_arg(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  90 {
  91     /* The fencer will filter "action" out when it registers the device,
  92      * but ignore it here in case any external API users don't.
  93      *
  94      * Also filter out parameters handled directly by Pacemaker.
  95      */
  96     if (!pcmk__str_eq(key, STONITH_ATTR_ACTION_OP, pcmk__str_casei)
  97         && !pcmk_stonith_param(key)
  98         && (strstr(key, CRM_META) == NULL)
  99         && !pcmk__str_eq(key, PCMK_XA_CRM_FEATURE_SET, pcmk__str_none)) {
 100 
 101         crm_trace("Passing %s=%s with fence action",
 102                   (const char *) key, (const char *) (value? value : ""));
 103         pcmk__insert_dup((GHashTable *) user_data, key, pcmk__s(value, ""));
 104     }
 105 }
 106 
 107 /*!
 108  * \internal
 109  * \brief Create a table of arguments for a fencing action
 110  *
 111  * \param[in] agent          Fencing agent name
 112  * \param[in] action         Name of fencing action
 113  * \param[in] target         Name of target node for fencing action
 114  * \param[in] target_nodeid  Node ID of target node for fencing action
 115  * \param[in] device_args    Fence device parameters
 116  * \param[in] port_map       Target node-to-port mapping for fence device
 117  * \param[in] host_arg       Argument name for passing target
 118  *
 119  * \return Newly created hash table of arguments for fencing action
 120  */
 121 static GHashTable *
 122 make_args(const char *agent, const char *action, const char *target,
     /* [previous][next][first][last][top][bottom][index][help] */
 123           uint32_t target_nodeid, GHashTable *device_args,
 124           GHashTable *port_map, const char *host_arg)
 125 {
 126     GHashTable *arg_list = NULL;
 127     const char *value = NULL;
 128 
 129     CRM_CHECK(action != NULL, return NULL);
 130 
 131     arg_list = pcmk__strkey_table(free, free);
 132 
 133     // Add action to arguments (using an alias if requested)
 134     if (device_args) {
 135         char buffer[512];
 136 
 137         snprintf(buffer, sizeof(buffer), "pcmk_%s_action", action);
 138         value = g_hash_table_lookup(device_args, buffer);
 139         if (value) {
 140             crm_debug("Substituting '%s' for fence action %s targeting %s",
 141                       value, action, pcmk__s(target, "no node"));
 142             action = value;
 143         }
 144     }
 145     pcmk__insert_dup(arg_list, STONITH_ATTR_ACTION_OP, action);
 146 
 147     /* If this is a fencing operation against another node, add more standard
 148      * arguments.
 149      */
 150     if ((target != NULL) && (device_args != NULL)) {
 151         const char *param = NULL;
 152 
 153         /* Always pass the target's name, per
 154          * https://github.com/ClusterLabs/fence-agents/blob/main/doc/FenceAgentAPI.md
 155          */
 156         pcmk__insert_dup(arg_list, "nodename", target);
 157 
 158         // If the target's node ID was specified, pass it, too
 159         if (target_nodeid != 0) {
 160             char *nodeid = crm_strdup_printf("%" PRIu32, target_nodeid);
 161 
 162             // cts-fencing looks for this log message
 163             crm_info("Passing '%s' as nodeid with fence action '%s' targeting %s",
 164                      nodeid, action, pcmk__s(target, "no node"));
 165             g_hash_table_insert(arg_list, strdup("nodeid"), nodeid);
 166         }
 167 
 168         // Check whether target must be specified in some other way
 169         param = g_hash_table_lookup(device_args, PCMK_STONITH_HOST_ARGUMENT);
 170         if (!pcmk__str_eq(agent, "fence_legacy", pcmk__str_none)
 171             && !pcmk__str_eq(param, PCMK_VALUE_NONE, pcmk__str_casei)) {
 172 
 173             if (param == NULL) {
 174                 /* Use the caller's default for pcmk_host_argument, or "port" if
 175                  * none was given
 176                  */
 177                 param = (host_arg == NULL)? "port" : host_arg;
 178             }
 179             value = g_hash_table_lookup(device_args, param);
 180 
 181             if (pcmk__str_eq(value, "dynamic",
 182                              pcmk__str_casei|pcmk__str_null_matches)) {
 183                 /* If the host argument was "dynamic" or not explicitly specified,
 184                  * add it with the target
 185                  */
 186                 const char *alias = NULL;
 187 
 188                 if (port_map) {
 189                     alias = g_hash_table_lookup(port_map, target);
 190                 }
 191                 if (alias == NULL) {
 192                     alias = target;
 193                 }
 194                 crm_debug("Passing %s='%s' with fence action %s targeting %s",
 195                           param, alias, action, pcmk__s(target, "no node"));
 196                 pcmk__insert_dup(arg_list, param, alias);
 197             }
 198         }
 199     }
 200 
 201     if (device_args) {
 202         g_hash_table_foreach(device_args, append_config_arg, arg_list);
 203     }
 204 
 205     return arg_list;
 206 }
 207 
 208 /*!
 209  * \internal
 210  * \brief Free all memory used by a stonith action
 211  *
 212  * \param[in,out] action  Action to free
 213  */
 214 void
 215 stonith__destroy_action(stonith_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 216 {
 217     if (action) {
 218         free(action->agent);
 219         if (action->args) {
 220             g_hash_table_destroy(action->args);
 221         }
 222         free(action->action);
 223         if (action->svc_action) {
 224             services_action_free(action->svc_action);
 225         }
 226         pcmk__reset_result(&(action->result));
 227         free(action);
 228     }
 229 }
 230 
 231 /*!
 232  * \internal
 233  * \brief Get the result of an executed stonith action
 234  *
 235  * \param[in] action  Executed action
 236  *
 237  * \return Pointer to action's result (or NULL if \p action is NULL)
 238  */
 239 pcmk__action_result_t *
 240 stonith__action_result(stonith_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 241 {
 242     return (action == NULL)? NULL : &(action->result);
 243 }
 244 
 245 #define FAILURE_MAX_RETRIES 2
 246 
 247 /*!
 248  * \internal
 249  * \brief Create a new fencing action to be executed
 250  *
 251  * \param[in] agent          Fence agent to use
 252  * \param[in] action_name    Fencing action to be executed
 253  * \param[in] target         Name of target of fencing action (if known)
 254  * \param[in] target_nodeid  Node ID of target of fencing action (if known)
 255  * \param[in] timeout_sec    Timeout to be used when executing action
 256  * \param[in] device_args    Parameters to pass to fence agent
 257  * \param[in] port_map       Mapping of target names to device ports
 258  * \param[in] host_arg       Agent parameter used to pass target name
 259  *
 260  * \return Newly created fencing action (asserts on error, never NULL)
 261  */
 262 stonith_action_t *
 263 stonith__action_create(const char *agent, const char *action_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 264                        const char *target, uint32_t target_nodeid,
 265                        int timeout_sec, GHashTable *device_args,
 266                        GHashTable *port_map, const char *host_arg)
 267 {
 268     stonith_action_t *action = pcmk__assert_alloc(1, sizeof(stonith_action_t));
 269 
 270     action->args = make_args(agent, action_name, target, target_nodeid,
 271                              device_args, port_map, host_arg);
 272     crm_debug("Preparing '%s' action targeting %s using agent %s",
 273               action_name, pcmk__s(target, "no node"), agent);
 274     action->agent = strdup(agent);
 275     action->action = strdup(action_name);
 276     action->timeout = action->remaining_timeout = timeout_sec;
 277     action->max_retries = FAILURE_MAX_RETRIES;
 278 
 279     pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN, PCMK_EXEC_UNKNOWN,
 280                      "Initialization bug in fencing library");
 281 
 282     if (device_args) {
 283         char buffer[512];
 284         const char *value = NULL;
 285 
 286         snprintf(buffer, sizeof(buffer), "pcmk_%s_retries", action_name);
 287         value = g_hash_table_lookup(device_args, buffer);
 288 
 289         if (value) {
 290             action->max_retries = atoi(value);
 291         }
 292     }
 293 
 294     return action;
 295 }
 296 
 297 static gboolean
 298 update_remaining_timeout(stonith_action_t * action)
     /* [previous][next][first][last][top][bottom][index][help] */
 299 {
 300     int diff = time(NULL) - action->initial_start_time;
 301 
 302     if (action->tries >= action->max_retries) {
 303         crm_info("Attempted to execute agent %s (%s) the maximum number of times (%d) allowed",
 304                  action->agent, action->action, action->max_retries);
 305         action->remaining_timeout = 0;
 306     } else if ((action->result.execution_status != PCMK_EXEC_TIMEOUT)
 307                && (diff < (action->timeout * 0.7))) {
 308         /* only set remaining timeout period if there is 30%
 309          * or greater of the original timeout period left */
 310         action->remaining_timeout = action->timeout - diff;
 311     } else {
 312         action->remaining_timeout = 0;
 313     }
 314     return action->remaining_timeout ? TRUE : FALSE;
 315 }
 316 
 317 /*!
 318  * \internal
 319  * \brief Map a fencing action result to a standard return code
 320  *
 321  * \param[in] result  Fencing action result to map
 322  *
 323  * \return Standard Pacemaker return code that best corresponds to \p result
 324  */
 325 int
 326 stonith__result2rc(const pcmk__action_result_t *result)
     /* [previous][next][first][last][top][bottom][index][help] */
 327 {
 328     if (pcmk__result_ok(result)) {
 329         return pcmk_rc_ok;
 330     }
 331 
 332     switch (result->execution_status) {
 333         case PCMK_EXEC_PENDING:         return EINPROGRESS;
 334         case PCMK_EXEC_CANCELLED:       return ECANCELED;
 335         case PCMK_EXEC_TIMEOUT:         return ETIME;
 336         case PCMK_EXEC_NOT_INSTALLED:   return ENOENT;
 337         case PCMK_EXEC_NOT_SUPPORTED:   return EOPNOTSUPP;
 338         case PCMK_EXEC_NOT_CONNECTED:   return ENOTCONN;
 339         case PCMK_EXEC_NO_FENCE_DEVICE: return ENODEV;
 340         case PCMK_EXEC_NO_SECRETS:      return EACCES;
 341 
 342         /* For the fencing API, PCMK_EXEC_INVALID is used with fencer API
 343          * operations that don't involve executing an agent (for example,
 344          * registering devices). This allows us to use the CRM_EX_* codes in the
 345          * exit status for finer-grained responses.
 346          */
 347         case PCMK_EXEC_INVALID:
 348             switch (result->exit_status) {
 349                 case CRM_EX_INVALID_PARAM:      return EINVAL;
 350                 case CRM_EX_INSUFFICIENT_PRIV:  return EACCES;
 351                 case CRM_EX_PROTOCOL:           return EPROTO;
 352 
 353                /* CRM_EX_EXPIRED is used for orphaned fencing operations left
 354                 * over from a previous instance of the fencer. For API backward
 355                 * compatibility, this is mapped to the previously used code for
 356                 * this case, EHOSTUNREACH.
 357                 */
 358                 case CRM_EX_EXPIRED:            return EHOSTUNREACH;
 359                 default:                        break;
 360             }
 361             break;
 362 
 363         default:
 364             break;
 365     }
 366 
 367     // Try to provide useful error code based on result's error output
 368 
 369     if (result->action_stderr == NULL) {
 370         return ENODATA;
 371 
 372     } else if (strcasestr(result->action_stderr, "timed out")
 373                || strcasestr(result->action_stderr, "timeout")) {
 374         return ETIME;
 375 
 376     } else if (strcasestr(result->action_stderr, "unrecognised action")
 377                || strcasestr(result->action_stderr, "unrecognized action")
 378                || strcasestr(result->action_stderr, "unsupported action")) {
 379         return EOPNOTSUPP;
 380     }
 381 
 382     // Oh well, we tried
 383     return pcmk_rc_error;
 384 }
 385 
 386 /*!
 387  * \internal
 388  * \brief Determine execution status equivalent of legacy fencer return code
 389  *
 390  * Fence action notifications, and fence action callbacks from older fencers
 391  * (<=2.1.2) in a rolling upgrade, will have only a legacy return code. Map this
 392  * to an execution status as best as possible (essentially, the inverse of
 393  * stonith__result2rc()).
 394  *
 395  * \param[in] rc           Legacy return code from fencer
 396  *
 397  * \return Execution status best corresponding to \p rc
 398  */
 399 int
 400 stonith__legacy2status(int rc)
     /* [previous][next][first][last][top][bottom][index][help] */
 401 {
 402     if (rc >= 0) {
 403         return PCMK_EXEC_DONE;
 404     }
 405     switch (-rc) {
 406         case EACCES:            return PCMK_EXEC_NO_SECRETS;
 407         case ECANCELED:         return PCMK_EXEC_CANCELLED;
 408         case EHOSTUNREACH:      return PCMK_EXEC_INVALID;
 409         case EINPROGRESS:       return PCMK_EXEC_PENDING;
 410         case ENODEV:            return PCMK_EXEC_NO_FENCE_DEVICE;
 411         case ENOENT:            return PCMK_EXEC_NOT_INSTALLED;
 412         case ENOTCONN:          return PCMK_EXEC_NOT_CONNECTED;
 413         case EOPNOTSUPP:        return PCMK_EXEC_NOT_SUPPORTED;
 414         case EPROTO:            return PCMK_EXEC_INVALID;
 415         case EPROTONOSUPPORT:   return PCMK_EXEC_NOT_SUPPORTED;
 416         case ETIME:             return PCMK_EXEC_TIMEOUT;
 417         case ETIMEDOUT:         return PCMK_EXEC_TIMEOUT;
 418         default:                return PCMK_EXEC_ERROR;
 419     }
 420 }
 421 
 422 /*!
 423  * \internal
 424  * \brief Add a fencing result to an XML element as attributes
 425  *
 426  * \param[in,out] xml     XML element to add result to
 427  * \param[in]     result  Fencing result to add (assume success if NULL)
 428  */
 429 void
 430 stonith__xe_set_result(xmlNode *xml, const pcmk__action_result_t *result)
     /* [previous][next][first][last][top][bottom][index][help] */
 431 {
 432     int exit_status = CRM_EX_OK;
 433     enum pcmk_exec_status execution_status = PCMK_EXEC_DONE;
 434     const char *exit_reason = NULL;
 435     const char *action_stdout = NULL;
 436     int rc = pcmk_ok;
 437 
 438     CRM_CHECK(xml != NULL, return);
 439 
 440     if (result != NULL) {
 441         exit_status = result->exit_status;
 442         execution_status = result->execution_status;
 443         exit_reason = result->exit_reason;
 444         action_stdout = result->action_stdout;
 445         rc = pcmk_rc2legacy(stonith__result2rc(result));
 446     }
 447 
 448     crm_xml_add_int(xml, PCMK__XA_OP_STATUS, (int) execution_status);
 449     crm_xml_add_int(xml, PCMK__XA_RC_CODE, exit_status);
 450     crm_xml_add(xml, PCMK_XA_EXIT_REASON, exit_reason);
 451     crm_xml_add(xml, PCMK__XA_ST_OUTPUT, action_stdout);
 452 
 453     /* @COMPAT Peers in rolling upgrades, Pacemaker Remote nodes, and external
 454      * code that use libstonithd <=2.1.2 don't check for the full result, and
 455      * need a legacy return code instead.
 456      */
 457     crm_xml_add_int(xml, PCMK__XA_ST_RC, rc);
 458 }
 459 
 460 /*!
 461  * \internal
 462  * \brief Find a fencing result beneath an XML element
 463  *
 464  * \param[in]  xml     XML element to search
 465  *
 466  * \return \p xml or descendant of it that contains a fencing result, else NULL
 467  */
 468 xmlNode *
 469 stonith__find_xe_with_result(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 470 {
 471     xmlNode *match = get_xpath_object("//@" PCMK__XA_RC_CODE, xml, LOG_NEVER);
 472 
 473     if (match == NULL) {
 474         /* @COMPAT Peers <=2.1.2 in a rolling upgrade provide only a legacy
 475          * return code, not a full result, so check for that.
 476          */
 477         match = get_xpath_object("//@" PCMK__XA_ST_RC, xml, LOG_ERR);
 478     }
 479     return match;
 480 }
 481 
 482 /*!
 483  * \internal
 484  * \brief Get a fencing result from an XML element's attributes
 485  *
 486  * \param[in]  xml     XML element with fencing result
 487  * \param[out] result  Where to store fencing result
 488  */
 489 void
 490 stonith__xe_get_result(const xmlNode *xml, pcmk__action_result_t *result)
     /* [previous][next][first][last][top][bottom][index][help] */
 491 {
 492     int exit_status = CRM_EX_OK;
 493     int execution_status = PCMK_EXEC_DONE;
 494     const char *exit_reason = NULL;
 495     char *action_stdout = NULL;
 496 
 497     CRM_CHECK((xml != NULL) && (result != NULL), return);
 498 
 499     exit_reason = crm_element_value(xml, PCMK_XA_EXIT_REASON);
 500     action_stdout = crm_element_value_copy(xml, PCMK__XA_ST_OUTPUT);
 501 
 502     // A result must include an exit status and execution status
 503     if ((crm_element_value_int(xml, PCMK__XA_RC_CODE, &exit_status) < 0)
 504         || (crm_element_value_int(xml, PCMK__XA_OP_STATUS,
 505                                   &execution_status) < 0)) {
 506         int rc = pcmk_ok;
 507         exit_status = CRM_EX_ERROR;
 508 
 509         /* @COMPAT Peers <=2.1.2 in rolling upgrades provide only a legacy
 510          * return code, not a full result, so check for that.
 511          */
 512         if (crm_element_value_int(xml, PCMK__XA_ST_RC, &rc) == 0) {
 513             if ((rc == pcmk_ok) || (rc == -EINPROGRESS)) {
 514                 exit_status = CRM_EX_OK;
 515             }
 516             execution_status = stonith__legacy2status(rc);
 517             exit_reason = pcmk_strerror(rc);
 518 
 519         } else {
 520             execution_status = PCMK_EXEC_ERROR;
 521             exit_reason = "Fencer reply contained neither a full result "
 522                           "nor a legacy return code (bug?)";
 523         }
 524     }
 525     pcmk__set_result(result, exit_status, execution_status, exit_reason);
 526     pcmk__set_result_output(result, action_stdout, NULL);
 527 }
 528 
 529 static void
 530 stonith_action_async_done(svc_action_t *svc_action)
     /* [previous][next][first][last][top][bottom][index][help] */
 531 {
 532     stonith_action_t *action = (stonith_action_t *) svc_action->cb_data;
 533 
 534     set_result_from_svc_action(action, svc_action);
 535     svc_action->params = NULL;
 536     log_action(action, action->pid);
 537 
 538     if (!pcmk__result_ok(&(action->result))
 539         && update_remaining_timeout(action)) {
 540 
 541         int rc = internal_stonith_action_execute(action);
 542         if (rc == pcmk_ok) {
 543             return;
 544         }
 545     }
 546 
 547     if (action->done_cb) {
 548         action->done_cb(action->pid, &(action->result), action->userdata);
 549     }
 550 
 551     action->svc_action = NULL; // don't remove our caller
 552     stonith__destroy_action(action);
 553 }
 554 
 555 static void
 556 stonith_action_async_forked(svc_action_t *svc_action)
     /* [previous][next][first][last][top][bottom][index][help] */
 557 {
 558     stonith_action_t *action = (stonith_action_t *) svc_action->cb_data;
 559 
 560     action->pid = svc_action->pid;
 561     action->svc_action = svc_action;
 562 
 563     if (action->fork_cb) {
 564         (action->fork_cb) (svc_action->pid, action->userdata);
 565     }
 566 
 567     pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN, PCMK_EXEC_PENDING,
 568                      NULL);
 569 
 570     crm_trace("Child process %d performing action '%s' successfully forked",
 571               action->pid, action->action);
 572 }
 573 
 574 static int
 575 internal_stonith_action_execute(stonith_action_t * action)
     /* [previous][next][first][last][top][bottom][index][help] */
 576 {
 577     int rc = -EPROTO;
 578     int is_retry = 0;
 579     svc_action_t *svc_action = NULL;
 580     static int stonith_sequence = 0;
 581     char *buffer = NULL;
 582 
 583     CRM_CHECK(action != NULL, return -EINVAL);
 584 
 585     if ((action->action == NULL) || (action->args == NULL)
 586         || (action->agent == NULL)) {
 587         pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN_ERROR,
 588                          PCMK_EXEC_ERROR_FATAL, "Bug in fencing library");
 589         return -EINVAL;
 590     }
 591 
 592     if (!action->tries) {
 593         action->initial_start_time = time(NULL);
 594     }
 595     action->tries++;
 596 
 597     if (action->tries > 1) {
 598         crm_info("Attempt %d to execute %s (%s). remaining timeout is %d",
 599                  action->tries, action->agent, action->action, action->remaining_timeout);
 600         is_retry = 1;
 601     }
 602 
 603     buffer = crm_strdup_printf(PCMK__FENCE_BINDIR "/%s",
 604                                basename(action->agent));
 605     svc_action = services_action_create_generic(buffer, NULL);
 606     free(buffer);
 607 
 608     if (svc_action->rc != PCMK_OCF_UNKNOWN) {
 609         set_result_from_svc_action(action, svc_action);
 610         services_action_free(svc_action);
 611         return -E2BIG;
 612     }
 613 
 614     svc_action->timeout = 1000 * action->remaining_timeout;
 615     svc_action->standard = strdup(PCMK_RESOURCE_CLASS_STONITH);
 616     svc_action->id = crm_strdup_printf("%s_%s_%dof%d", basename(action->agent),
 617                                        action->action, action->tries,
 618                                        action->max_retries);
 619     svc_action->agent = strdup(action->agent);
 620     svc_action->sequence = stonith_sequence++;
 621     svc_action->params = action->args;
 622     svc_action->cb_data = (void *) action;
 623     svc_action->flags = pcmk__set_flags_as(__func__, __LINE__,
 624                                            LOG_TRACE, "Action",
 625                                            svc_action->id, svc_action->flags,
 626                                            SVC_ACTION_NON_BLOCKED,
 627                                            "SVC_ACTION_NON_BLOCKED");
 628 
 629     /* keep retries from executing out of control and free previous results */
 630     if (is_retry) {
 631         pcmk__reset_result(&(action->result));
 632         sleep(1);
 633     }
 634 
 635     if (action->async) {
 636         // We never create a recurring action, so this should always return TRUE
 637         CRM_LOG_ASSERT(services_action_async_fork_notify(svc_action,
 638                                               &stonith_action_async_done,
 639                                               &stonith_action_async_forked));
 640         return pcmk_ok;
 641 
 642     } else if (services_action_sync(svc_action)) { // sync success
 643         rc = pcmk_ok;
 644 
 645     } else { // sync failure
 646         rc = -ECONNABORTED;
 647     }
 648 
 649     set_result_from_svc_action(action, svc_action);
 650     svc_action->params = NULL;
 651     services_action_free(svc_action);
 652     return rc;
 653 }
 654 
 655 /*!
 656  * \internal
 657  * \brief Kick off execution of an async stonith action
 658  *
 659  * \param[in,out] action        Action to be executed
 660  * \param[in,out] userdata      Datapointer to be passed to callbacks
 661  * \param[in]     done          Callback to notify action has failed/succeeded
 662  * \param[in]     fork_callback Callback to notify successful fork of child
 663  *
 664  * \return pcmk_ok if ownership of action has been taken, -errno otherwise
 665  */
 666 int
 667 stonith__execute_async(stonith_action_t * action, void *userdata,
     /* [previous][next][first][last][top][bottom][index][help] */
 668                        void (*done) (int pid,
 669                                      const pcmk__action_result_t *result,
 670                                      void *user_data),
 671                        void (*fork_cb) (int pid, void *user_data))
 672 {
 673     if (!action) {
 674         return -EINVAL;
 675     }
 676 
 677     action->userdata = userdata;
 678     action->done_cb = done;
 679     action->fork_cb = fork_cb;
 680     action->async = true;
 681 
 682     return internal_stonith_action_execute(action);
 683 }
 684 
 685 /*!
 686  * \internal
 687  * \brief Execute a stonith action
 688  *
 689  * \param[in,out] action  Action to execute
 690  *
 691  * \return pcmk_ok on success, -errno otherwise
 692  */
 693 int
 694 stonith__execute(stonith_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 695 {
 696     int rc = pcmk_ok;
 697 
 698     CRM_CHECK(action != NULL, return -EINVAL);
 699 
 700     // Keep trying until success, max retries, or timeout
 701     do {
 702         rc = internal_stonith_action_execute(action);
 703     } while ((rc != pcmk_ok) && update_remaining_timeout(action));
 704 
 705     return rc;
 706 }

/* [previous][next][first][last][top][bottom][index][help] */