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_action_execute_async
  18. stonith__execute

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

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