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-2023 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/stonith-ng.h>
  20 #include <crm/fencing/internal.h>
  21 
  22 #include "fencing_private.h"
  23 
  24 #define RH_STONITH_PREFIX "fence_"
  25 
  26 /*!
  27  * \internal
  28  * \brief Add available RHCS-compatible agents to a list
  29  *
  30  * \param[in,out]  List to add to
  31  *
  32  * \return Number of agents added
  33  */
  34 int
  35 stonith__list_rhcs_agents(stonith_key_value_t **devices)
     /* [previous][next][first][last][top][bottom][index][help] */
  36 {
  37     // Essentially: ls -1 @sbin_dir@/fence_*
  38 
  39     int count = 0, i;
  40     struct dirent **namelist;
  41     const int file_num = scandir(PCMK__FENCE_BINDIR, &namelist, 0, alphasort);
  42 
  43 #if _POSIX_C_SOURCE < 200809L && !(defined(O_SEARCH) || defined(O_PATH))
  44     char buffer[FILENAME_MAX + 1];
  45 #elif defined(O_SEARCH)
  46     const int dirfd = open(PCMK__FENCE_BINDIR, O_SEARCH);
  47 #else
  48     const int dirfd = open(PCMK__FENCE_BINDIR, O_PATH);
  49 #endif
  50 
  51     for (i = 0; i < file_num; i++) {
  52         struct stat prop;
  53 
  54         if (pcmk__starts_with(namelist[i]->d_name, RH_STONITH_PREFIX)) {
  55 #if _POSIX_C_SOURCE < 200809L && !(defined(O_SEARCH) || defined(O_PATH))
  56             snprintf(buffer, sizeof(buffer), "%s/%s", PCMK__FENCE_BINDIR,
  57                      namelist[i]->d_name);
  58             if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) {
  59 #else
  60             if (dirfd == -1) {
  61                 if (i == 0) {
  62                     crm_notice("Problem with listing %s directory"
  63                                CRM_XS "errno=%d", RH_STONITH_PREFIX, errno);
  64                 }
  65                 free(namelist[i]);
  66                 continue;
  67             }
  68             /* note: we can possibly prevent following symlinks here,
  69                      which may be a good idea, but fall on the nose when
  70                      these agents are moved elsewhere & linked back */
  71             if (fstatat(dirfd, namelist[i]->d_name, &prop, 0) == 0
  72                     && S_ISREG(prop.st_mode)) {
  73 #endif
  74                 *devices = stonith_key_value_add(*devices, NULL,
  75                                                  namelist[i]->d_name);
  76                 count++;
  77             }
  78         }
  79         free(namelist[i]);
  80     }
  81     if (file_num > 0) {
  82         free(namelist);
  83     }
  84 #if _POSIX_C_SOURCE >= 200809L || defined(O_SEARCH) || defined(O_PATH)
  85     if (dirfd >= 0) {
  86         close(dirfd);
  87     }
  88 #endif
  89     return count;
  90 }
  91 
  92 static void
  93 stonith_rhcs_parameter_not_required(xmlNode *metadata, const char *parameter)
     /* [previous][next][first][last][top][bottom][index][help] */
  94 {
  95     char *xpath = NULL;
  96     xmlXPathObject *xpathObj = NULL;
  97 
  98     CRM_CHECK(metadata != NULL, return);
  99     CRM_CHECK(parameter != NULL, return);
 100 
 101     xpath = crm_strdup_printf("//parameter[@name='%s']", parameter);
 102     /* Fudge metadata so that the parameter isn't required in config
 103      * Pacemaker handles and adds it */
 104     xpathObj = xpath_search(metadata, xpath);
 105     if (numXpathResults(xpathObj) > 0) {
 106         xmlNode *tmp = getXpathResult(xpathObj, 0);
 107 
 108         crm_xml_add(tmp, "required", "0");
 109     }
 110     freeXpathObject(xpathObj);
 111     free(xpath);
 112 }
 113 
 114 /*!
 115  * \brief Execute RHCS-compatible agent's metadata action
 116  *
 117  * \param[in]  agent        Agent to execute
 118  * \param[in]  timeout_sec  Action timeout
 119  * \param[out] metadata     Where to store output xmlNode (or NULL to ignore)
 120  */
 121 static int
 122 stonith__rhcs_get_metadata(const char *agent, int timeout_sec,
     /* [previous][next][first][last][top][bottom][index][help] */
 123                            xmlNode **metadata)
 124 {
 125     xmlNode *xml = NULL;
 126     xmlNode *actions = NULL;
 127     xmlXPathObject *xpathObj = NULL;
 128     stonith_action_t *action = stonith__action_create(agent, "metadata", NULL,
 129                                                       0, timeout_sec, NULL,
 130                                                       NULL, NULL);
 131     int rc = stonith__execute(action);
 132     pcmk__action_result_t *result = stonith__action_result(action);
 133 
 134     if (result == NULL) {
 135         if (rc < 0) {
 136             crm_warn("Could not execute metadata action for %s: %s "
 137                      CRM_XS " rc=%d", agent, pcmk_strerror(rc), rc);
 138         }
 139         stonith__destroy_action(action);
 140         return rc;
 141     }
 142 
 143     if (result->execution_status != PCMK_EXEC_DONE) {
 144         crm_warn("Could not execute metadata action for %s: %s",
 145                  agent, pcmk_exec_status_str(result->execution_status));
 146         rc = pcmk_rc2legacy(stonith__result2rc(result));
 147         stonith__destroy_action(action);
 148         return rc;
 149     }
 150 
 151     if (!pcmk__result_ok(result)) {
 152         crm_warn("Metadata action for %s returned error code %d",
 153                  agent, result->exit_status);
 154         rc = pcmk_rc2legacy(stonith__result2rc(result));
 155         stonith__destroy_action(action);
 156         return rc;
 157     }
 158 
 159     if (result->action_stdout == NULL) {
 160         crm_warn("Metadata action for %s returned no data", agent);
 161         stonith__destroy_action(action);
 162         return -ENODATA;
 163     }
 164 
 165     xml = string2xml(result->action_stdout);
 166     stonith__destroy_action(action);
 167 
 168     if (xml == NULL) {
 169         crm_warn("Metadata for %s is invalid", agent);
 170         return -pcmk_err_schema_validation;
 171     }
 172 
 173     xpathObj = xpath_search(xml, "//actions");
 174     if (numXpathResults(xpathObj) > 0) {
 175         actions = getXpathResult(xpathObj, 0);
 176     }
 177     freeXpathObject(xpathObj);
 178 
 179     // Add start and stop (implemented by pacemaker, not agent) to meta-data
 180     xpathObj = xpath_search(xml, "//action[@name='stop']");
 181     if (numXpathResults(xpathObj) <= 0) {
 182         xmlNode *tmp = NULL;
 183         const char *timeout_str = NULL;
 184 
 185         timeout_str = pcmk__readable_interval(PCMK_DEFAULT_ACTION_TIMEOUT_MS);
 186 
 187         tmp = create_xml_node(actions, "action");
 188         crm_xml_add(tmp, "name", PCMK_ACTION_STOP);
 189         crm_xml_add(tmp, "timeout", timeout_str);
 190 
 191         tmp = create_xml_node(actions, "action");
 192         crm_xml_add(tmp, "name", PCMK_ACTION_START);
 193         crm_xml_add(tmp, "timeout", timeout_str);
 194     }
 195     freeXpathObject(xpathObj);
 196 
 197     // Fudge metadata so parameters are not required in config (pacemaker adds them)
 198     stonith_rhcs_parameter_not_required(xml, "action");
 199     stonith_rhcs_parameter_not_required(xml, "plug");
 200     stonith_rhcs_parameter_not_required(xml, "port");
 201 
 202     if (metadata) {
 203         *metadata = xml;
 204 
 205     } else {
 206         free_xml(xml);
 207     }
 208 
 209     return pcmk_ok;
 210 }
 211 
 212 /*!
 213  * \brief Retrieve metadata for RHCS-compatible fence agent
 214  *
 215  * \param[in]  agent        Agent to execute
 216  * \param[in]  timeout_sec  Action timeout
 217  * \param[out] output       Where to store action output (or NULL to ignore)
 218  */
 219 int
 220 stonith__rhcs_metadata(const char *agent, int timeout_sec, char **output)
     /* [previous][next][first][last][top][bottom][index][help] */
 221 {
 222     char *buffer = NULL;
 223     xmlNode *xml = NULL;
 224 
 225     int rc = stonith__rhcs_get_metadata(agent, timeout_sec, &xml);
 226 
 227     if (rc != pcmk_ok) {
 228         free_xml(xml);
 229         return rc;
 230     }
 231 
 232     buffer = dump_xml_formatted_with_text(xml);
 233     free_xml(xml);
 234     if (buffer == NULL) {
 235         return -pcmk_err_schema_validation;
 236     }
 237     if (output) {
 238         *output = buffer;
 239     } else {
 240         free(buffer);
 241     }
 242     return pcmk_ok;
 243 }
 244 
 245 bool
 246 stonith__agent_is_rhcs(const char *agent)
     /* [previous][next][first][last][top][bottom][index][help] */
 247 {
 248     struct stat prop;
 249     char *buffer = crm_strdup_printf(PCMK__FENCE_BINDIR "/%s", agent);
 250     int rc = stat(buffer, &prop);
 251 
 252     free(buffer);
 253     return (rc >= 0) && S_ISREG(prop.st_mode);
 254 }
 255 
 256 int
 257 stonith__rhcs_validate(stonith_t *st, int call_options, const char *target,
     /* [previous][next][first][last][top][bottom][index][help] */
 258                        const char *agent, GHashTable *params,
 259                        const char * host_arg, int timeout,
 260                        char **output, char **error_output)
 261 {
 262     int rc = pcmk_ok;
 263     int remaining_timeout = timeout;
 264     xmlNode *metadata = NULL;
 265     stonith_action_t *action = NULL;
 266     pcmk__action_result_t *result = NULL;
 267 
 268     if (host_arg == NULL) {
 269         time_t start_time = time(NULL);
 270 
 271         rc = stonith__rhcs_get_metadata(agent, remaining_timeout, &metadata);
 272 
 273         if (rc == pcmk_ok) {
 274             uint32_t device_flags = 0;
 275 
 276             stonith__device_parameter_flags(&device_flags, agent, metadata);
 277             if (pcmk_is_set(device_flags, st_device_supports_parameter_port)) {
 278                 host_arg = "port";
 279 
 280             } else if (pcmk_is_set(device_flags,
 281                                    st_device_supports_parameter_plug)) {
 282                 host_arg = "plug";
 283             }
 284         }
 285 
 286         free_xml(metadata);
 287 
 288         remaining_timeout -= time(NULL) - start_time;
 289 
 290         if (rc == -ETIME || remaining_timeout <= 0 ) {
 291             return -ETIME;
 292         }
 293 
 294     } else if (pcmk__str_eq(host_arg, PCMK__VALUE_NONE, pcmk__str_casei)) {
 295         host_arg = NULL;
 296     }
 297 
 298     action = stonith__action_create(agent, PCMK_ACTION_VALIDATE_ALL, target, 0,
 299                                     remaining_timeout, params, NULL, host_arg);
 300 
 301     rc = stonith__execute(action);
 302     result = stonith__action_result(action);
 303 
 304     if (result != NULL) {
 305         rc = pcmk_rc2legacy(stonith__result2rc(result));
 306 
 307         // Take ownership of output so stonith__destroy_action() doesn't free it
 308         if (output != NULL) {
 309             *output = result->action_stdout;
 310             result->action_stdout = NULL;
 311         }
 312         if (error_output != NULL) {
 313             *error_output = result->action_stderr;
 314             result->action_stderr = NULL;
 315         }
 316     }
 317     stonith__destroy_action(action);
 318     return rc;
 319 }

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