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. stonith_action_to_svc
  17. internal_stonith_action_execute
  18. stonith__execute_async
  19. stonith__execute

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

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