root/lib/fencing/st_rhcs.c

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

DEFINITIONS

This source file includes following definitions.
  1. rhcs_agent_filter
  2. stonith__list_rhcs_agents
  3. stonith_rhcs_parameter_not_required
  4. stonith__rhcs_get_metadata
  5. stonith__rhcs_metadata
  6. stonith__agent_is_rhcs
  7. stonith__rhcs_validate

   1 /*
   2  * Copyright 2004-2025 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <dirent.h>
  13 #include <stdio.h>
  14 #include <string.h>
  15 #include <sys/stat.h>
  16 
  17 #include <glib.h>
  18 #include <libxml/xpath.h>           // xmlXPathObject, etc.
  19 
  20 #include <crm/crm.h>
  21 #include <crm/common/xml.h>
  22 #include <crm/stonith-ng.h>
  23 #include <crm/fencing/internal.h>
  24 
  25 #include "fencing_private.h"
  26 
  27 /*!
  28  * \internal
  29  * \brief \c scandir() filter for RHCS fence agents
  30  *
  31  * \param[in] entry  Directory entry
  32  *
  33  * \retval 1 if \p entry is a regular file whose name begins with \c "fence_"
  34  * \retval 0 otherwise
  35  */
  36 static int
  37 rhcs_agent_filter(const struct dirent *entry)
     /* [previous][next][first][last][top][bottom][index][help] */
  38 {
  39     char *buf = NULL;
  40     struct stat sb;
  41     int rc = 0;
  42 
  43     if (!pcmk__starts_with(entry->d_name, "fence_")) {
  44         goto done;
  45     }
  46 
  47     // glibc doesn't enforce PATH_MAX, so don't limit buf size
  48     buf = crm_strdup_printf(PCMK__FENCE_BINDIR "/%s", entry->d_name);
  49     if ((stat(buf, &sb) != 0) || !S_ISREG(sb.st_mode)) {
  50         goto done;
  51     }
  52 
  53     rc = 1;
  54 
  55 done:
  56     free(buf);
  57     return rc;
  58 }
  59 
  60 /*!
  61  * \internal
  62  * \brief Add available RHCS-compatible agents to a list
  63  *
  64  * \param[in,out]  List to add to
  65  *
  66  * \return Number of agents added
  67  */
  68 int
  69 stonith__list_rhcs_agents(stonith_key_value_t **devices)
     /* [previous][next][first][last][top][bottom][index][help] */
  70 {
  71     struct dirent **namelist = NULL;
  72     const int file_num = scandir(PCMK__FENCE_BINDIR, &namelist,
  73                                  rhcs_agent_filter, alphasort);
  74 
  75     if (file_num < 0) {
  76         int rc = errno;
  77 
  78         crm_err("Could not list " PCMK__FENCE_BINDIR ": %s", pcmk_rc_str(rc));
  79         free(namelist);
  80         return 0;
  81     }
  82 
  83     for (int i = 0; i < file_num; i++) {
  84         *devices = stonith__key_value_add(*devices, NULL, namelist[i]->d_name);
  85         free(namelist[i]);
  86     }
  87     free(namelist);
  88     return file_num;
  89 }
  90 
  91 static void
  92 stonith_rhcs_parameter_not_required(xmlNode *metadata, const char *parameter)
     /* [previous][next][first][last][top][bottom][index][help] */
  93 {
  94     char *xpath = NULL;
  95     xmlXPathObject *xpathObj = NULL;
  96 
  97     CRM_CHECK(metadata != NULL, return);
  98     CRM_CHECK(parameter != NULL, return);
  99 
 100     xpath = crm_strdup_printf("//" PCMK_XE_PARAMETER "[@" PCMK_XA_NAME "='%s']",
 101                               parameter);
 102     /* Fudge metadata so that the parameter isn't required in config
 103      * Pacemaker handles and adds it */
 104     xpathObj = pcmk__xpath_search(metadata->doc, xpath);
 105     if (pcmk__xpath_num_results(xpathObj) > 0) {
 106         xmlNode *tmp = pcmk__xpath_result(xpathObj, 0);
 107 
 108         if (tmp != NULL) {
 109             crm_xml_add(tmp, "required", "0");
 110         }
 111     }
 112     xmlXPathFreeObject(xpathObj);
 113     free(xpath);
 114 }
 115 
 116 /*!
 117  * \brief Execute RHCS-compatible agent's metadata action
 118  *
 119  * \param[in]  agent        Agent to execute
 120  * \param[in]  timeout_sec  Action timeout
 121  * \param[out] metadata     Where to store output xmlNode (or NULL to ignore)
 122  */
 123 static int
 124 stonith__rhcs_get_metadata(const char *agent, int timeout_sec,
     /* [previous][next][first][last][top][bottom][index][help] */
 125                            xmlNode **metadata)
 126 {
 127     xmlNode *xml = NULL;
 128     xmlNode *actions = NULL;
 129     xmlXPathObject *xpathObj = NULL;
 130     stonith_action_t *action = stonith__action_create(agent,
 131                                                       PCMK_ACTION_METADATA,
 132                                                       NULL, timeout_sec, NULL,
 133                                                       NULL, NULL);
 134     int rc = stonith__execute(action);
 135     pcmk__action_result_t *result = stonith__action_result(action);
 136 
 137     if (result == NULL) {
 138         if (rc < 0) {
 139             crm_warn("Could not execute metadata action for %s: %s "
 140                      QB_XS " rc=%d", agent, pcmk_strerror(rc), rc);
 141         }
 142         stonith__destroy_action(action);
 143         return rc;
 144     }
 145 
 146     if (result->execution_status != PCMK_EXEC_DONE) {
 147         crm_warn("Could not execute metadata action for %s: %s",
 148                  agent, pcmk_exec_status_str(result->execution_status));
 149         rc = pcmk_rc2legacy(stonith__result2rc(result));
 150         stonith__destroy_action(action);
 151         return rc;
 152     }
 153 
 154     if (!pcmk__result_ok(result)) {
 155         crm_warn("Metadata action for %s returned error code %d",
 156                  agent, result->exit_status);
 157         rc = pcmk_rc2legacy(stonith__result2rc(result));
 158         stonith__destroy_action(action);
 159         return rc;
 160     }
 161 
 162     if (result->action_stdout == NULL) {
 163         crm_warn("Metadata action for %s returned no data", agent);
 164         stonith__destroy_action(action);
 165         return -ENODATA;
 166     }
 167 
 168     xml = pcmk__xml_parse(result->action_stdout);
 169     stonith__destroy_action(action);
 170 
 171     if (xml == NULL) {
 172         crm_warn("Metadata for %s is invalid", agent);
 173         return -pcmk_err_schema_validation;
 174     }
 175 
 176     xpathObj = pcmk__xpath_search(xml->doc, "//" PCMK_XE_ACTIONS);
 177     if (pcmk__xpath_num_results(xpathObj) > 0) {
 178         actions = pcmk__xpath_result(xpathObj, 0);
 179     }
 180     xmlXPathFreeObject(xpathObj);
 181 
 182     // Add start and stop (implemented by pacemaker, not agent) to meta-data
 183     xpathObj = pcmk__xpath_search(xml->doc,
 184                                   "//" PCMK_XE_ACTION
 185                                   "[@" PCMK_XA_NAME "='" PCMK_ACTION_STOP "']");
 186     if (pcmk__xpath_num_results(xpathObj) == 0) {
 187         xmlNode *tmp = NULL;
 188         const char *timeout_str = NULL;
 189 
 190         timeout_str = pcmk__readable_interval(PCMK_DEFAULT_ACTION_TIMEOUT_MS);
 191 
 192         tmp = pcmk__xe_create(actions, PCMK_XE_ACTION);
 193         crm_xml_add(tmp, PCMK_XA_NAME, PCMK_ACTION_STOP);
 194         crm_xml_add(tmp, PCMK_META_TIMEOUT, timeout_str);
 195 
 196         tmp = pcmk__xe_create(actions, PCMK_XE_ACTION);
 197         crm_xml_add(tmp, PCMK_XA_NAME, PCMK_ACTION_START);
 198         crm_xml_add(tmp, PCMK_META_TIMEOUT, timeout_str);
 199     }
 200     xmlXPathFreeObject(xpathObj);
 201 
 202     // Fudge metadata so parameters are not required in config (pacemaker adds them)
 203     stonith_rhcs_parameter_not_required(xml, STONITH_ATTR_ACTION_OP);
 204     stonith_rhcs_parameter_not_required(xml, "plug");
 205     stonith_rhcs_parameter_not_required(xml, "port");
 206 
 207     if (metadata) {
 208         *metadata = xml;
 209 
 210     } else {
 211         pcmk__xml_free(xml);
 212     }
 213 
 214     return pcmk_ok;
 215 }
 216 
 217 /*!
 218  * \brief Retrieve metadata for RHCS-compatible fence agent
 219  *
 220  * \param[in]  agent        Agent to execute
 221  * \param[in]  timeout_sec  Action timeout
 222  * \param[out] output       Where to store action output (or NULL to ignore)
 223  */
 224 int
 225 stonith__rhcs_metadata(const char *agent, int timeout_sec, char **output)
     /* [previous][next][first][last][top][bottom][index][help] */
 226 {
 227     GString *buffer = NULL;
 228     xmlNode *xml = NULL;
 229 
 230     int rc = stonith__rhcs_get_metadata(agent, timeout_sec, &xml);
 231 
 232     if (rc != pcmk_ok) {
 233         goto done;
 234     }
 235 
 236     buffer = g_string_sized_new(1024);
 237     pcmk__xml_string(xml, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, buffer, 0);
 238 
 239     if (pcmk__str_empty(buffer->str)) {
 240         rc = -pcmk_err_schema_validation;
 241         goto done;
 242     }
 243 
 244     if (output != NULL) {
 245         pcmk__str_update(output, buffer->str);
 246     }
 247 
 248 done:
 249     if (buffer != NULL) {
 250         g_string_free(buffer, TRUE);
 251     }
 252     pcmk__xml_free(xml);
 253     return rc;
 254 }
 255 
 256 bool
 257 stonith__agent_is_rhcs(const char *agent)
     /* [previous][next][first][last][top][bottom][index][help] */
 258 {
 259     struct stat prop;
 260     char *buffer = crm_strdup_printf(PCMK__FENCE_BINDIR "/%s", agent);
 261     int rc = stat(buffer, &prop);
 262 
 263     free(buffer);
 264     return (rc >= 0) && S_ISREG(prop.st_mode);
 265 }
 266 
 267 int
 268 stonith__rhcs_validate(stonith_t *st, int call_options, const char *target,
     /* [previous][next][first][last][top][bottom][index][help] */
 269                        const char *agent, GHashTable *params,
 270                        const char * host_arg, int timeout,
 271                        char **output, char **error_output)
 272 {
 273     int rc = pcmk_ok;
 274     int remaining_timeout = timeout;
 275     xmlNode *metadata = NULL;
 276     stonith_action_t *action = NULL;
 277     pcmk__action_result_t *result = NULL;
 278 
 279     if (host_arg == NULL) {
 280         time_t start_time = time(NULL);
 281 
 282         rc = stonith__rhcs_get_metadata(agent, remaining_timeout, &metadata);
 283 
 284         if (rc == pcmk_ok) {
 285             host_arg = stonith__default_host_arg(metadata);
 286             crm_trace("Using '%s' as default " PCMK_STONITH_HOST_ARGUMENT
 287                       " for %s",
 288                       pcmk__s(host_arg, PCMK_VALUE_NONE), agent);
 289         }
 290 
 291         pcmk__xml_free(metadata);
 292 
 293         remaining_timeout -= time(NULL) - start_time;
 294 
 295         if (rc == -ETIME || remaining_timeout <= 0 ) {
 296             return -ETIME;
 297         }
 298 
 299     } else if (pcmk__str_eq(host_arg, PCMK_VALUE_NONE, pcmk__str_casei)) {
 300         host_arg = NULL;
 301     }
 302 
 303     action = stonith__action_create(agent, PCMK_ACTION_VALIDATE_ALL, target,
 304                                     remaining_timeout, params, NULL, host_arg);
 305 
 306     rc = stonith__execute(action);
 307     result = stonith__action_result(action);
 308 
 309     if (result != NULL) {
 310         rc = pcmk_rc2legacy(stonith__result2rc(result));
 311 
 312         // Take ownership of output so stonith__destroy_action() doesn't free it
 313         if (output != NULL) {
 314             *output = result->action_stdout;
 315             result->action_stdout = NULL;
 316         }
 317         if (error_output != NULL) {
 318             *error_output = result->action_stderr;
 319             result->action_stderr = NULL;
 320         }
 321     }
 322     stonith__destroy_action(action);
 323     return rc;
 324 }

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