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

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