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

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