diff --git a/lib/fencing/st_lha.c b/lib/fencing/st_lha.c index 8224914cd5..f8e96656c3 100644 --- a/lib/fencing/st_lha.c +++ b/lib/fencing/st_lha.c @@ -1,286 +1,286 @@ /* * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fencing_private.h" #define LHA_STONITH_LIBRARY "libstonith.so.1" static void *lha_agents_lib = NULL; // @TODO Use XML string constants and maybe a real XML object static const char META_TEMPLATE[] = "\n" "<" PCMK_XE_RESOURCE_AGENT " " PCMK_XA_NAME "=\"%s\">\n" - " <" PCMK_XE_VERSION ">1.0\n" + " <" PCMK_XE_VERSION ">1.1\n" " <" PCMK_XE_LONGDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">\n" "%s\n" " \n" " <" PCMK_XE_SHORTDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">" "%s" "\n" "%s\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " <" PCMK_XE_VERSION ">2.0\n" " \n" "\n"; static void * find_library_function(void **handle, const char *lib, const char *fn) { void *a_function; if (*handle == NULL) { *handle = dlopen(lib, RTLD_LAZY); if ((*handle) == NULL) { crm_err("Could not open %s: %s", lib, dlerror()); return NULL; } } a_function = dlsym(*handle, fn); if (a_function == NULL) { crm_err("Could not find %s in %s: %s", fn, lib, dlerror()); } return a_function; } /*! * \internal * \brief Check whether a given fence agent is an LHA agent * * \param[in] agent Fence agent type * * \return true if \p agent is an LHA agent, otherwise false */ bool stonith__agent_is_lha(const char *agent) { Stonith *stonith_obj = NULL; static bool need_init = true; static Stonith *(*st_new_fn) (const char *) = NULL; static void (*st_del_fn) (Stonith *) = NULL; if (need_init) { need_init = false; st_new_fn = find_library_function(&lha_agents_lib, LHA_STONITH_LIBRARY, "stonith_new"); st_del_fn = find_library_function(&lha_agents_lib, LHA_STONITH_LIBRARY, "stonith_delete"); } if (lha_agents_lib && st_new_fn && st_del_fn) { stonith_obj = (*st_new_fn) (agent); if (stonith_obj) { (*st_del_fn) (stonith_obj); return true; } } return false; } int stonith__list_lha_agents(stonith_key_value_t **devices) { static gboolean need_init = TRUE; int count = 0; char **entry = NULL; char **type_list = NULL; static char **(*type_list_fn) (void) = NULL; static void (*type_free_fn) (char **) = NULL; if (need_init) { need_init = FALSE; type_list_fn = find_library_function(&lha_agents_lib, LHA_STONITH_LIBRARY, "stonith_types"); type_free_fn = find_library_function(&lha_agents_lib, LHA_STONITH_LIBRARY, "stonith_free_hostlist"); } if (type_list_fn) { type_list = (*type_list_fn) (); } for (entry = type_list; entry != NULL && *entry; ++entry) { crm_trace("Added: %s", *entry); *devices = stonith_key_value_add(*devices, NULL, *entry); count++; } if (type_list && type_free_fn) { (*type_free_fn) (type_list); } return count; } static void stonith_plugin(int priority, const char *fmt, ...) G_GNUC_PRINTF(2, 3); static void stonith_plugin(int priority, const char *format, ...) { int err = errno; va_list ap; int len = 0; char *string = NULL; va_start(ap, format); len = vasprintf (&string, format, ap); va_end(ap); CRM_ASSERT(len > 0); do_crm_log_alias(priority, __FILE__, __func__, __LINE__, "%s", string); free(string); errno = err; } int stonith__lha_metadata(const char *agent, int timeout, char **output) { int rc = 0; char *buffer = NULL; static const char *no_parameter_info = ""; Stonith *stonith_obj = NULL; static gboolean need_init = TRUE; static Stonith *(*st_new_fn) (const char *) = NULL; static const char *(*st_info_fn) (Stonith *, int) = NULL; static void (*st_del_fn) (Stonith *) = NULL; static void (*st_log_fn) (Stonith *, PILLogFun) = NULL; if (need_init) { need_init = FALSE; st_new_fn = find_library_function(&lha_agents_lib, LHA_STONITH_LIBRARY, "stonith_new"); st_del_fn = find_library_function(&lha_agents_lib, LHA_STONITH_LIBRARY, "stonith_delete"); st_log_fn = find_library_function(&lha_agents_lib, LHA_STONITH_LIBRARY, "stonith_set_log"); st_info_fn = find_library_function(&lha_agents_lib, LHA_STONITH_LIBRARY, "stonith_get_info"); } if (lha_agents_lib && st_new_fn && st_del_fn && st_info_fn && st_log_fn) { char *xml_meta_longdesc = NULL; char *xml_meta_shortdesc = NULL; char *meta_param = NULL; char *meta_longdesc = NULL; char *meta_shortdesc = NULL; const char *timeout_str = NULL; stonith_obj = (*st_new_fn) (agent); if (stonith_obj) { (*st_log_fn) (stonith_obj, (PILLogFun) & stonith_plugin); pcmk__str_update(&meta_longdesc, (*st_info_fn) (stonith_obj, ST_DEVICEDESCR)); if (meta_longdesc == NULL) { crm_warn("no long description in %s's metadata.", agent); meta_longdesc = strdup(no_parameter_info); } pcmk__str_update(&meta_shortdesc, (*st_info_fn) (stonith_obj, ST_DEVICEID)); if (meta_shortdesc == NULL) { crm_warn("no short description in %s's metadata.", agent); meta_shortdesc = strdup(no_parameter_info); } pcmk__str_update(&meta_param, (*st_info_fn) (stonith_obj, ST_CONF_XML)); if (meta_param == NULL) { crm_warn("no list of parameters in %s's metadata.", agent); meta_param = strdup(no_parameter_info); } (*st_del_fn) (stonith_obj); } else { errno = EINVAL; crm_perror(LOG_ERR, "Agent %s not found", agent); return -EINVAL; } xml_meta_longdesc = (char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_longdesc); xml_meta_shortdesc = (char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_shortdesc); timeout_str = pcmk__readable_interval(PCMK_DEFAULT_ACTION_TIMEOUT_MS); buffer = crm_strdup_printf(META_TEMPLATE, agent, xml_meta_longdesc, xml_meta_shortdesc, meta_param, timeout_str, timeout_str, timeout_str); xmlFree(xml_meta_longdesc); xmlFree(xml_meta_shortdesc); free(meta_shortdesc); free(meta_longdesc); free(meta_param); } if (output) { *output = buffer; } else { free(buffer); } return rc; } /* Implement a dummy function that uses -lpils so that linkers don't drop the * reference. */ #include const char *i_hate_pils(int rc); const char * i_hate_pils(int rc) { return PIL_strerror(rc); } int stonith__lha_validate(stonith_t *st, int call_options, const char *target, const char *agent, GHashTable *params, int timeout, char **output, char **error_output) { errno = EOPNOTSUPP; crm_perror(LOG_ERR, "Cannot validate Linux-HA fence agents"); return -EOPNOTSUPP; } diff --git a/lib/services/services_lsb.c b/lib/services/services_lsb.c index b95c4940a3..aca365626a 100644 --- a/lib/services/services_lsb.c +++ b/lib/services/services_lsb.c @@ -1,344 +1,344 @@ /* * Copyright 2010-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include "services_private.h" #include "services_lsb.h" // @TODO Use XML string constants and maybe a real XML object #define lsb_metadata_template \ "\n" \ "<" PCMK_XE_RESOURCE_AGENT " " \ PCMK_XA_NAME "='%s' " \ PCMK_XA_VERSION "='" PCMK_DEFAULT_AGENT_VERSION "'>\n" \ - " <" PCMK_XE_VERSION ">1.0\n" \ + " <" PCMK_XE_VERSION ">1.1\n" \ " <" PCMK_XE_LONGDESC " " PCMK_XA_LANG "='" PCMK__VALUE_EN "'>\n" \ "%s" \ " \n" \ " <" PCMK_XE_SHORTDESC " " PCMK_XA_LANG "='" PCMK__VALUE_EN "'>" \ "%s" \ "\n" \ " <" PCMK_XE_PARAMETERS "/>\n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " %s\n" \ " %s\n" \ " %s\n" \ " %s\n" \ " %s\n" \ " %s\n" \ " %s\n" \ " \n" \ "\n" /* See "Comment Conventions for Init Scripts" in the LSB core specification at: * http://refspecs.linuxfoundation.org/lsb.shtml */ #define LSB_INITSCRIPT_INFOBEGIN_TAG "### BEGIN INIT INFO" #define LSB_INITSCRIPT_INFOEND_TAG "### END INIT INFO" #define PROVIDES "# Provides:" #define REQUIRED_START "# Required-Start:" #define REQUIRED_STOP "# Required-Stop:" #define SHOULD_START "# Should-Start:" #define SHOULD_STOP "# Should-Stop:" #define DEFAULT_START "# Default-Start:" #define DEFAULT_STOP "# Default-Stop:" #define SHORT_DESC "# Short-Description:" #define DESCRIPTION "# Description:" #define lsb_meta_helper_free_value(m) \ do { \ if ((m) != NULL) { \ xmlFree(m); \ (m) = NULL; \ } \ } while(0) /*! * \internal * \brief Grab an LSB header value * * \param[in] line Line read from LSB init script * \param[in,out] value If not set, will be set to XML-safe copy of value * \param[in] prefix Set value if line starts with this pattern * * \return TRUE if value was set, FALSE otherwise */ static inline gboolean lsb_meta_helper_get_value(const char *line, char **value, const char *prefix) { if (!*value && pcmk__starts_with(line, prefix)) { *value = (char *)xmlEncodeEntitiesReentrant(NULL, BAD_CAST line+strlen(prefix)); return TRUE; } return FALSE; } int services__get_lsb_metadata(const char *type, char **output) { char ra_pathname[PATH_MAX] = { 0, }; FILE *fp = NULL; char buffer[1024] = { 0, }; char *provides = NULL; char *required_start = NULL; char *required_stop = NULL; char *should_start = NULL; char *should_stop = NULL; char *default_start = NULL; char *default_stop = NULL; char *short_desc = NULL; char *long_desc = NULL; bool in_header = FALSE; if (type[0] == '/') { snprintf(ra_pathname, sizeof(ra_pathname), "%s", type); } else { snprintf(ra_pathname, sizeof(ra_pathname), "%s/%s", PCMK__LSB_INIT_DIR, type); } crm_trace("Looking into %s", ra_pathname); fp = fopen(ra_pathname, "r"); if (fp == NULL) { return -errno; } /* Enter into the LSB-compliant comment block */ while (fgets(buffer, sizeof(buffer), fp)) { // Ignore lines up to and including the block delimiter if (pcmk__starts_with(buffer, LSB_INITSCRIPT_INFOBEGIN_TAG)) { in_header = TRUE; continue; } if (!in_header) { continue; } /* Assume each of the following eight arguments contain one line */ if (lsb_meta_helper_get_value(buffer, &provides, PROVIDES)) { continue; } if (lsb_meta_helper_get_value(buffer, &required_start, REQUIRED_START)) { continue; } if (lsb_meta_helper_get_value(buffer, &required_stop, REQUIRED_STOP)) { continue; } if (lsb_meta_helper_get_value(buffer, &should_start, SHOULD_START)) { continue; } if (lsb_meta_helper_get_value(buffer, &should_stop, SHOULD_STOP)) { continue; } if (lsb_meta_helper_get_value(buffer, &default_start, DEFAULT_START)) { continue; } if (lsb_meta_helper_get_value(buffer, &default_stop, DEFAULT_STOP)) { continue; } if (lsb_meta_helper_get_value(buffer, &short_desc, SHORT_DESC)) { continue; } /* Long description may cross multiple lines */ if ((long_desc == NULL) // Haven't already found long description && pcmk__starts_with(buffer, DESCRIPTION)) { bool processed_line = TRUE; GString *desc = g_string_sized_new(2048); // Get remainder of description line itself g_string_append(desc, buffer + sizeof(DESCRIPTION) - 1); // Read any continuation lines of the description buffer[0] = '\0'; while (fgets(buffer, sizeof(buffer), fp)) { if (pcmk__starts_with(buffer, "# ") || pcmk__starts_with(buffer, "#\t")) { /* '#' followed by a tab or more than one space indicates a * continuation of the long description. */ g_string_append(desc, buffer + 1); } else { /* This line is not part of the long description, * so continue with normal processing. */ processed_line = FALSE; break; } } // Make long description safe to use in XML long_desc = (char *) xmlEncodeEntitiesReentrant(NULL, (pcmkXmlStr) desc->str); g_string_free(desc, TRUE); if (processed_line) { // We grabbed the line into the long description continue; } } // Stop if we leave the header block if (pcmk__starts_with(buffer, LSB_INITSCRIPT_INFOEND_TAG)) { break; } if (buffer[0] != '#') { break; } } fclose(fp); *output = crm_strdup_printf(lsb_metadata_template, type, pcmk__s(long_desc, type), pcmk__s(short_desc, type), pcmk__s(provides, ""), pcmk__s(required_start, ""), pcmk__s(required_stop, ""), pcmk__s(should_start, ""), pcmk__s(should_stop, ""), pcmk__s(default_start, ""), pcmk__s(default_stop, "")); lsb_meta_helper_free_value(long_desc); lsb_meta_helper_free_value(short_desc); lsb_meta_helper_free_value(provides); lsb_meta_helper_free_value(required_start); lsb_meta_helper_free_value(required_stop); lsb_meta_helper_free_value(should_start); lsb_meta_helper_free_value(should_stop); lsb_meta_helper_free_value(default_start); lsb_meta_helper_free_value(default_stop); return pcmk_ok; } GList * services__list_lsb_agents(void) { return services_os_get_directory_list(PCMK__LSB_INIT_DIR, TRUE, TRUE); } bool services__lsb_agent_exists(const char *agent) { bool rc = FALSE; struct stat st; char *path = pcmk__full_path(agent, PCMK__LSB_INIT_DIR); rc = (stat(path, &st) == 0); free(path); return rc; } /*! * \internal * \brief Prepare an LSB action * * \param[in,out] op Action to prepare * * \return Standard Pacemaker return code */ int services__lsb_prepare(svc_action_t *op) { op->opaque->exec = pcmk__full_path(op->agent, PCMK__LSB_INIT_DIR); op->opaque->args[0] = strdup(op->opaque->exec); op->opaque->args[1] = strdup(op->action); if ((op->opaque->args[0] == NULL) || (op->opaque->args[1] == NULL)) { return ENOMEM; } return pcmk_rc_ok; } /*! * \internal * \brief Map an LSB result to a standard OCF result * * \param[in] action Action that result is for * \param[in] exit_status LSB agent exit status * * \return Standard OCF result */ enum ocf_exitcode services__lsb2ocf(const char *action, int exit_status) { // For non-status actions, LSB and OCF share error codes <= 7 if (!pcmk__str_any_of(action, PCMK_ACTION_STATUS, PCMK_ACTION_MONITOR, NULL)) { if ((exit_status < 0) || (exit_status > PCMK_LSB_NOT_RUNNING)) { return PCMK_OCF_UNKNOWN_ERROR; } return (enum ocf_exitcode) exit_status; } // LSB status actions have their own codes switch (exit_status) { case PCMK_LSB_STATUS_OK: return PCMK_OCF_OK; case PCMK_LSB_STATUS_NOT_INSTALLED: return PCMK_OCF_NOT_INSTALLED; case PCMK_LSB_STATUS_INSUFFICIENT_PRIV: return PCMK_OCF_INSUFFICIENT_PRIV; case PCMK_LSB_STATUS_VAR_PID: case PCMK_LSB_STATUS_VAR_LOCK: case PCMK_LSB_STATUS_NOT_RUNNING: return PCMK_OCF_NOT_RUNNING; default: return PCMK_OCF_UNKNOWN_ERROR; } } // Deprecated functions kept only for backward API compatibility // LCOV_EXCL_START #include svc_action_t * services_action_create(const char *name, const char *action, guint interval_ms, int timeout) { return resources_action_create(name, PCMK_RESOURCE_CLASS_LSB, NULL, name, action, interval_ms, timeout, NULL, 0); } GList * services_list(void) { return resources_list_agents(PCMK_RESOURCE_CLASS_LSB, NULL); } // LCOV_EXCL_STOP // End deprecated API