LCOV - code coverage report
Current view: top level - fencing - st_actions.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 268 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 18 0.0 %

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

Generated by: LCOV version 1.14