root/lib/fencing/st_rhcs.c

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

DEFINITIONS

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

   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 <stdio.h>
  13 #include <string.h>
  14 #include <sys/stat.h>
  15 #include <glib.h>
  16 #include <dirent.h>
  17 
  18 #include <crm/crm.h>
  19 #include <crm/common/xml.h>
  20 #include <crm/stonith-ng.h>
  21 #include <crm/fencing/internal.h>
  22 
  23 #include "fencing_private.h"
  24 
  25 #define RH_STONITH_PREFIX "fence_"
  26 
  27 /*!
  28  * \internal
  29  * \brief Add available RHCS-compatible agents to a list
  30  *
  31  * \param[in,out]  List to add to
  32  *
  33  * \return Number of agents added
  34  */
  35 int
  36 stonith__list_rhcs_agents(stonith_key_value_t **devices)
     /* [previous][next][first][last][top][bottom][index][help] */
  37 {
  38     // Essentially: ls -1 @sbin_dir@/fence_*
  39 
  40     int count = 0, i;
  41     struct dirent **namelist;
  42     const int file_num = scandir(PCMK__FENCE_BINDIR, &namelist, 0, alphasort);
  43 
  44 #if _POSIX_C_SOURCE < 200809L && !(defined(O_SEARCH) || defined(O_PATH))
  45     char buffer[FILENAME_MAX + 1];
  46 #elif defined(O_SEARCH)
  47     const int dirfd = open(PCMK__FENCE_BINDIR, O_SEARCH);
  48 #else
  49     const int dirfd = open(PCMK__FENCE_BINDIR, O_PATH);
  50 #endif
  51 
  52     for (i = 0; i < file_num; i++) {
  53         struct stat prop;
  54 
  55         if (pcmk__starts_with(namelist[i]->d_name, RH_STONITH_PREFIX)) {
  56 #if _POSIX_C_SOURCE < 200809L && !(defined(O_SEARCH) || defined(O_PATH))
  57             snprintf(buffer, sizeof(buffer), "%s/%s", PCMK__FENCE_BINDIR,
  58                      namelist[i]->d_name);
  59             if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) {
  60 #else
  61             if (dirfd == -1) {
  62                 if (i == 0) {
  63                     crm_notice("Problem with listing %s directory "
  64                                CRM_XS " errno=%d", RH_STONITH_PREFIX, errno);
  65                 }
  66                 free(namelist[i]);
  67                 continue;
  68             }
  69             /* note: we can possibly prevent following symlinks here,
  70                      which may be a good idea, but fall on the nose when
  71                      these agents are moved elsewhere & linked back */
  72             if (fstatat(dirfd, namelist[i]->d_name, &prop, 0) == 0
  73                     && S_ISREG(prop.st_mode)) {
  74 #endif
  75                 *devices = stonith_key_value_add(*devices, NULL,
  76                                                  namelist[i]->d_name);
  77                 count++;
  78             }
  79         }
  80         free(namelist[i]);
  81     }
  82     if (file_num > 0) {
  83         free(namelist);
  84     }
  85 #if _POSIX_C_SOURCE >= 200809L || defined(O_SEARCH) || defined(O_PATH)
  86     if (dirfd >= 0) {
  87         close(dirfd);
  88     }
  89 #endif
  90     return count;
  91 }
  92 
  93 static void
  94 stonith_rhcs_parameter_not_required(xmlNode *metadata, const char *parameter)
     /* [previous][next][first][last][top][bottom][index][help] */
  95 {
  96     char *xpath = NULL;
  97     xmlXPathObject *xpathObj = NULL;
  98 
  99     CRM_CHECK(metadata != NULL, return);
 100     CRM_CHECK(parameter != NULL, return);
 101 
 102     xpath = crm_strdup_printf("//" PCMK_XE_PARAMETER "[@" PCMK_XA_NAME "='%s']",
 103                               parameter);
 104     /* Fudge metadata so that the parameter isn't required in config
 105      * Pacemaker handles and adds it */
 106     xpathObj = xpath_search(metadata, xpath);
 107     if (numXpathResults(xpathObj) > 0) {
 108         xmlNode *tmp = getXpathResult(xpathObj, 0);
 109 
 110         crm_xml_add(tmp, "required", "0");
 111     }
 112     freeXpathObject(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, 0, timeout_sec,
 133                                                       NULL, 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                      CRM_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 = xpath_search(xml, "//" PCMK_XE_ACTIONS);
 177     if (numXpathResults(xpathObj) > 0) {
 178         actions = getXpathResult(xpathObj, 0);
 179     }
 180     freeXpathObject(xpathObj);
 181 
 182     // Add start and stop (implemented by pacemaker, not agent) to meta-data
 183     xpathObj = xpath_search(xml,
 184                             "//" PCMK_XE_ACTION
 185                             "[@" PCMK_XA_NAME "='" PCMK_ACTION_STOP "']");
 186     if (numXpathResults(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     freeXpathObject(xpathObj);
 201 
 202     // Fudge metadata so parameters are not required in config (pacemaker adds them)
 203     stonith_rhcs_parameter_not_required(xml, "action");
 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         free_xml(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     free_xml(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             uint32_t device_flags = 0;
 286 
 287             stonith__device_parameter_flags(&device_flags, agent, metadata);
 288             if (pcmk_is_set(device_flags, st_device_supports_parameter_port)) {
 289                 host_arg = "port";
 290 
 291             } else if (pcmk_is_set(device_flags,
 292                                    st_device_supports_parameter_plug)) {
 293                 host_arg = "plug";
 294             }
 295         }
 296 
 297         free_xml(metadata);
 298 
 299         remaining_timeout -= time(NULL) - start_time;
 300 
 301         if (rc == -ETIME || remaining_timeout <= 0 ) {
 302             return -ETIME;
 303         }
 304 
 305     } else if (pcmk__str_eq(host_arg, PCMK_VALUE_NONE, pcmk__str_casei)) {
 306         host_arg = NULL;
 307     }
 308 
 309     action = stonith__action_create(agent, PCMK_ACTION_VALIDATE_ALL, target, 0,
 310                                     remaining_timeout, params, NULL, host_arg);
 311 
 312     rc = stonith__execute(action);
 313     result = stonith__action_result(action);
 314 
 315     if (result != NULL) {
 316         rc = pcmk_rc2legacy(stonith__result2rc(result));
 317 
 318         // Take ownership of output so stonith__destroy_action() doesn't free it
 319         if (output != NULL) {
 320             *output = result->action_stdout;
 321             result->action_stdout = NULL;
 322         }
 323         if (error_output != NULL) {
 324             *error_output = result->action_stderr;
 325             result->action_stderr = NULL;
 326         }
 327     }
 328     stonith__destroy_action(action);
 329     return rc;
 330 }

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