diff options
Diffstat (limited to 'lib/services/services_lsb.c')
-rw-r--r-- | lib/services/services_lsb.c | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/lib/services/services_lsb.c b/lib/services/services_lsb.c new file mode 100644 index 0000000..134cc70 --- /dev/null +++ b/lib/services/services_lsb.c @@ -0,0 +1,341 @@ +/* + * Copyright 2010-2022 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 <crm_internal.h> + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include <stdio.h> +#include <errno.h> +#include <sys/stat.h> + +#include <crm/crm.h> +#include <crm/services.h> +#include "services_private.h" +#include "services_lsb.h" + +#define lsb_metadata_template \ + "<?xml version='1.0'?>\n" \ + "<!DOCTYPE resource-agent SYSTEM 'ra-api-1.dtd'>\n" \ + "<resource-agent name='%s' version='" PCMK_DEFAULT_AGENT_VERSION "'>\n" \ + " <version>1.0</version>\n" \ + " <longdesc lang='en'>\n" \ + "%s" \ + " </longdesc>\n" \ + " <shortdesc lang='en'>%s</shortdesc>\n" \ + " <parameters>\n" \ + " </parameters>\n" \ + " <actions>\n" \ + " <action name='meta-data' timeout='5' />\n" \ + " <action name='start' timeout='15' />\n" \ + " <action name='stop' timeout='15' />\n" \ + " <action name='status' timeout='15' />\n" \ + " <action name='restart' timeout='15' />\n" \ + " <action name='force-reload' timeout='15' />\n" \ + " <action name='monitor' timeout='15' interval='15' />\n" \ + " </actions>\n" \ + " <special tag='LSB'>\n" \ + " <Provides>%s</Provides>\n" \ + " <Required-Start>%s</Required-Start>\n" \ + " <Required-Stop>%s</Required-Stop>\n" \ + " <Should-Start>%s</Should-Start>\n" \ + " <Should-Stop>%s</Should-Stop>\n" \ + " <Default-Start>%s</Default-Start>\n" \ + " <Default-Stop>%s</Default-Stop>\n" \ + " </special>\n" \ + "</resource-agent>\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 REQ_START "# Required-Start:" +#define REQ_STOP "# Required-Stop:" +#define SHLD_START "# Should-Start:" +#define SHLD_STOP "# Should-Stop:" +#define DFLT_START "# Default-Start:" +#define DFLT_STOP "# Default-Stop:" +#define SHORT_DSCR "# 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 *req_start = NULL; + char *req_stop = NULL; + char *shld_start = NULL; + char *shld_stop = NULL; + char *dflt_start = NULL; + char *dflt_stop = NULL; + char *s_dscrpt = NULL; + char *xml_l_dscrpt = 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, &req_start, REQ_START)) { + continue; + } + if (lsb_meta_helper_get_value(buffer, &req_stop, REQ_STOP)) { + continue; + } + if (lsb_meta_helper_get_value(buffer, &shld_start, SHLD_START)) { + continue; + } + if (lsb_meta_helper_get_value(buffer, &shld_stop, SHLD_STOP)) { + continue; + } + if (lsb_meta_helper_get_value(buffer, &dflt_start, DFLT_START)) { + continue; + } + if (lsb_meta_helper_get_value(buffer, &dflt_stop, DFLT_STOP)) { + continue; + } + if (lsb_meta_helper_get_value(buffer, &s_dscrpt, SHORT_DSCR)) { + continue; + } + + /* Long description may cross multiple lines */ + if ((xml_l_dscrpt == 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 + xml_l_dscrpt = + (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, + (xml_l_dscrpt? xml_l_dscrpt : type), + (s_dscrpt? s_dscrpt : type), + (provides? provides : ""), + (req_start? req_start : ""), + (req_stop? req_stop : ""), + (shld_start? shld_start : ""), + (shld_stop? shld_stop : ""), + (dflt_start? dflt_start : ""), + (dflt_stop? dflt_stop : "")); + + lsb_meta_helper_free_value(xml_l_dscrpt); + lsb_meta_helper_free_value(s_dscrpt); + lsb_meta_helper_free_value(provides); + lsb_meta_helper_free_value(req_start); + lsb_meta_helper_free_value(req_stop); + lsb_meta_helper_free_value(shld_start); + lsb_meta_helper_free_value(shld_stop); + lsb_meta_helper_free_value(dflt_start); + lsb_meta_helper_free_value(dflt_stop); + + crm_trace("Created fake metadata: %llu", + (unsigned long long) strlen(*output)); + 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, "status", "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 <crm/services_compat.h> + +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 |