root/lib/services/services_lsb.c

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

DEFINITIONS

This source file includes following definitions.
  1. lsb_meta_helper_get_value
  2. services__get_lsb_metadata
  3. services__list_lsb_agents
  4. services__lsb_agent_exists
  5. services__lsb_prepare
  6. services__lsb2ocf

   1 /*
   2  * Copyright 2010-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 <errno.h>
  14 #include <sys/stat.h>
  15 
  16 #include <crm/crm.h>
  17 #include <crm/common/xml.h>
  18 #include <crm/services.h>
  19 #include "services_private.h"
  20 #include "services_lsb.h"
  21 
  22 // @TODO Use XML string constants and maybe a real XML object
  23 #define lsb_metadata_template  \
  24     "<?xml " PCMK_XA_VERSION "='1.0'?>\n"                                     \
  25     "<" PCMK_XE_RESOURCE_AGENT " "                                            \
  26         PCMK_XA_NAME "='%s' "                                                 \
  27         PCMK_XA_VERSION "='" PCMK_DEFAULT_AGENT_VERSION "'>\n"                \
  28     "  <" PCMK_XE_VERSION ">1.1</" PCMK_XE_VERSION ">\n"                      \
  29     "  <" PCMK_XE_LONGDESC " " PCMK_XA_LANG "='" PCMK__VALUE_EN "'>\n"        \
  30         "%s"                                                                  \
  31     "  </" PCMK_XE_LONGDESC ">\n"                                             \
  32     "  <" PCMK_XE_SHORTDESC " " PCMK_XA_LANG "='" PCMK__VALUE_EN "'>"         \
  33         "%s"                                                                  \
  34       "</" PCMK_XE_SHORTDESC ">\n"                                            \
  35     "  <" PCMK_XE_PARAMETERS "/>\n"                                           \
  36     "  <" PCMK_XE_ACTIONS ">\n"                                               \
  37     "    <" PCMK_XE_ACTION " " PCMK_XA_NAME "='" PCMK_ACTION_META_DATA "'"    \
  38                            " " PCMK_META_TIMEOUT "='5s' />\n"                 \
  39     "    <" PCMK_XE_ACTION " " PCMK_XA_NAME "='" PCMK_ACTION_START "'"        \
  40                            " " PCMK_META_TIMEOUT "='15s' />\n"                \
  41     "    <" PCMK_XE_ACTION " " PCMK_XA_NAME "='" PCMK_ACTION_STOP "'"         \
  42                            " " PCMK_META_TIMEOUT "='15s' />\n"                \
  43     "    <" PCMK_XE_ACTION " " PCMK_XA_NAME "='" PCMK_ACTION_STATUS "'"       \
  44                            " " PCMK_META_TIMEOUT "='15s' />\n"                \
  45     "    <" PCMK_XE_ACTION " " PCMK_XA_NAME "='restart'"                      \
  46                            " " PCMK_META_TIMEOUT "='15s' />\n"                \
  47     "    <" PCMK_XE_ACTION " " PCMK_XA_NAME "='force-reload'"                 \
  48                            " " PCMK_META_TIMEOUT "='15s' />\n"                \
  49     "    <" PCMK_XE_ACTION " " PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "'"      \
  50                            " " PCMK_META_TIMEOUT "='15s'"                     \
  51                            " " PCMK_META_INTERVAL "='15s' />\n"               \
  52     "  </" PCMK_XE_ACTIONS ">\n"                                              \
  53     "  <" PCMK_XE_SPECIAL " " PCMK_XA_TAG "='LSB'>\n"                         \
  54     "    <Provides>%s</Provides>\n"                                           \
  55     "    <Required-Start>%s</Required-Start>\n"                               \
  56     "    <Required-Stop>%s</Required-Stop>\n"                                 \
  57     "    <Should-Start>%s</Should-Start>\n"                                   \
  58     "    <Should-Stop>%s</Should-Stop>\n"                                     \
  59     "    <Default-Start>%s</Default-Start>\n"                                 \
  60     "    <Default-Stop>%s</Default-Stop>\n"                                   \
  61     "  </" PCMK_XE_SPECIAL ">\n"                                              \
  62     "</" PCMK_XE_RESOURCE_AGENT ">\n"
  63 
  64 /* See "Comment Conventions for Init Scripts" in the LSB core specification at:
  65  * http://refspecs.linuxfoundation.org/lsb.shtml
  66  */
  67 #define LSB_INITSCRIPT_INFOBEGIN_TAG    "### BEGIN INIT INFO"
  68 #define LSB_INITSCRIPT_INFOEND_TAG      "### END INIT INFO"
  69 #define PROVIDES                        "# Provides:"
  70 #define REQUIRED_START                  "# Required-Start:"
  71 #define REQUIRED_STOP                   "# Required-Stop:"
  72 #define SHOULD_START                    "# Should-Start:"
  73 #define SHOULD_STOP                     "# Should-Stop:"
  74 #define DEFAULT_START                   "# Default-Start:"
  75 #define DEFAULT_STOP                    "# Default-Stop:"
  76 #define SHORT_DESC                      "# Short-Description:"
  77 #define DESCRIPTION                     "# Description:"
  78 
  79 /*!
  80  * \internal
  81  * \brief Grab an LSB header value
  82  *
  83  * \param[in]     line    Line read from LSB init script
  84  * \param[in,out] value   If not set, will be set to XML-safe copy of value
  85  * \param[in]     prefix  Set value if line starts with this pattern
  86  *
  87  * \return TRUE if value was set, FALSE otherwise
  88  */
  89 static inline gboolean
  90 lsb_meta_helper_get_value(const char *line, gchar **value, const char *prefix)
     /* [previous][next][first][last][top][bottom][index][help] */
  91 {
  92     /* @TODO Perhaps update later to use pcmk__xml_needs_escape(). Involves many
  93      * extra variables in the caller.
  94      */
  95     if ((*value == NULL) && pcmk__starts_with(line, prefix)) {
  96         *value = pcmk__xml_escape(line + strlen(prefix), pcmk__xml_escape_text);
  97         return TRUE;
  98     }
  99     return FALSE;
 100 }
 101 
 102 int
 103 services__get_lsb_metadata(const char *type, char **output)
     /* [previous][next][first][last][top][bottom][index][help] */
 104 {
 105     char ra_pathname[PATH_MAX] = { 0, };
 106     FILE *fp = NULL;
 107     char buffer[1024] = { 0, };
 108     gchar *provides = NULL;
 109     gchar *required_start = NULL;
 110     gchar *required_stop = NULL;
 111     gchar *should_start = NULL;
 112     gchar *should_stop = NULL;
 113     gchar *default_start = NULL;
 114     gchar *default_stop = NULL;
 115     gchar *short_desc = NULL;
 116     gchar *long_desc = NULL;
 117     bool in_header = FALSE;
 118 
 119     if (type[0] == '/') {
 120         snprintf(ra_pathname, sizeof(ra_pathname), "%s", type);
 121     } else {
 122         snprintf(ra_pathname, sizeof(ra_pathname), "%s/%s",
 123                  PCMK__LSB_INIT_DIR, type);
 124     }
 125 
 126     crm_trace("Looking into %s", ra_pathname);
 127     fp = fopen(ra_pathname, "r");
 128     if (fp == NULL) {
 129         return -errno;
 130     }
 131 
 132     /* Enter into the LSB-compliant comment block */
 133     while (fgets(buffer, sizeof(buffer), fp)) {
 134 
 135         // Ignore lines up to and including the block delimiter
 136         if (pcmk__starts_with(buffer, LSB_INITSCRIPT_INFOBEGIN_TAG)) {
 137             in_header = TRUE;
 138             continue;
 139         }
 140         if (!in_header) {
 141             continue;
 142         }
 143 
 144         /* Assume each of the following eight arguments contain one line */
 145         if (lsb_meta_helper_get_value(buffer, &provides, PROVIDES)) {
 146             continue;
 147         }
 148         if (lsb_meta_helper_get_value(buffer, &required_start,
 149                                       REQUIRED_START)) {
 150             continue;
 151         }
 152         if (lsb_meta_helper_get_value(buffer, &required_stop, REQUIRED_STOP)) {
 153             continue;
 154         }
 155         if (lsb_meta_helper_get_value(buffer, &should_start, SHOULD_START)) {
 156             continue;
 157         }
 158         if (lsb_meta_helper_get_value(buffer, &should_stop, SHOULD_STOP)) {
 159             continue;
 160         }
 161         if (lsb_meta_helper_get_value(buffer, &default_start, DEFAULT_START)) {
 162             continue;
 163         }
 164         if (lsb_meta_helper_get_value(buffer, &default_stop, DEFAULT_STOP)) {
 165             continue;
 166         }
 167         if (lsb_meta_helper_get_value(buffer, &short_desc, SHORT_DESC)) {
 168             continue;
 169         }
 170 
 171         /* Long description may cross multiple lines */
 172         if ((long_desc == NULL)  // Haven't already found long description
 173             && pcmk__starts_with(buffer, DESCRIPTION)) {
 174             bool processed_line = TRUE;
 175             GString *desc = g_string_sized_new(2048);
 176 
 177             // Get remainder of description line itself
 178             g_string_append(desc, buffer + sizeof(DESCRIPTION) - 1);
 179 
 180             // Read any continuation lines of the description
 181             buffer[0] = '\0';
 182             while (fgets(buffer, sizeof(buffer), fp)) {
 183                 if (pcmk__starts_with(buffer, "#  ")
 184                     || pcmk__starts_with(buffer, "#\t")) {
 185                     /* '#' followed by a tab or more than one space indicates a
 186                      * continuation of the long description.
 187                      */
 188                     g_string_append(desc, buffer + 1);
 189                 } else {
 190                     /* This line is not part of the long description,
 191                      * so continue with normal processing.
 192                      */
 193                     processed_line = FALSE;
 194                     break;
 195                 }
 196             }
 197 
 198             // Make long description safe to use in XML
 199             long_desc = pcmk__xml_escape(desc->str, pcmk__xml_escape_text);
 200             g_string_free(desc, TRUE);
 201 
 202             if (processed_line) {
 203                 // We grabbed the line into the long description
 204                 continue;
 205             }
 206         }
 207 
 208         // Stop if we leave the header block
 209         if (pcmk__starts_with(buffer, LSB_INITSCRIPT_INFOEND_TAG)) {
 210             break;
 211         }
 212         if (buffer[0] != '#') {
 213             break;
 214         }
 215     }
 216     fclose(fp);
 217 
 218     *output = crm_strdup_printf(lsb_metadata_template, type,
 219                                 pcmk__s(long_desc, type),
 220                                 pcmk__s(short_desc, type),
 221                                 pcmk__s(provides, ""),
 222                                 pcmk__s(required_start, ""),
 223                                 pcmk__s(required_stop, ""),
 224                                 pcmk__s(should_start, ""),
 225                                 pcmk__s(should_stop, ""),
 226                                 pcmk__s(default_start, ""),
 227                                 pcmk__s(default_stop, ""));
 228 
 229     g_free(long_desc);
 230     g_free(short_desc);
 231     g_free(provides);
 232     g_free(required_start);
 233     g_free(required_stop);
 234     g_free(should_start);
 235     g_free(should_stop);
 236     g_free(default_start);
 237     g_free(default_stop);
 238     return pcmk_ok;
 239 }
 240 
 241 GList *
 242 services__list_lsb_agents(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 243 {
 244     return services_os_get_directory_list(PCMK__LSB_INIT_DIR, TRUE, TRUE);
 245 }
 246 
 247 bool
 248 services__lsb_agent_exists(const char *agent)
     /* [previous][next][first][last][top][bottom][index][help] */
 249 {
 250     bool rc = FALSE;
 251     struct stat st;
 252     char *path = pcmk__full_path(agent, PCMK__LSB_INIT_DIR);
 253 
 254     rc = (stat(path, &st) == 0);
 255     free(path);
 256     return rc;
 257 }
 258 
 259 /*!
 260  * \internal
 261  * \brief Prepare an LSB action
 262  *
 263  * \param[in,out] op  Action to prepare
 264  *
 265  * \return Standard Pacemaker return code
 266  */
 267 int
 268 services__lsb_prepare(svc_action_t *op)
     /* [previous][next][first][last][top][bottom][index][help] */
 269 {
 270     op->opaque->exec = pcmk__full_path(op->agent, PCMK__LSB_INIT_DIR);
 271     op->opaque->args[0] = strdup(op->opaque->exec);
 272     op->opaque->args[1] = strdup(op->action);
 273     if ((op->opaque->args[0] == NULL) || (op->opaque->args[1] == NULL)) {
 274         return ENOMEM;
 275     }
 276     return pcmk_rc_ok;
 277 }
 278 
 279 /*!
 280  * \internal
 281  * \brief Map an LSB result to a standard OCF result
 282  *
 283  * \param[in] action       Action that result is for
 284  * \param[in] exit_status  LSB agent exit status
 285  *
 286  * \return Standard OCF result
 287  */
 288 enum ocf_exitcode
 289 services__lsb2ocf(const char *action, int exit_status)
     /* [previous][next][first][last][top][bottom][index][help] */
 290 {
 291     // For non-status actions, LSB and OCF share error codes <= 7
 292     if (!pcmk__str_any_of(action, PCMK_ACTION_STATUS, PCMK_ACTION_MONITOR,
 293                           NULL)) {
 294         if ((exit_status < 0) || (exit_status > PCMK_LSB_NOT_RUNNING)) {
 295             return PCMK_OCF_UNKNOWN_ERROR;
 296         }
 297         return (enum ocf_exitcode) exit_status;
 298     }
 299 
 300     // LSB status actions have their own codes
 301     switch (exit_status) {
 302         case PCMK_LSB_STATUS_OK:
 303             return PCMK_OCF_OK;
 304 
 305         case PCMK_LSB_STATUS_NOT_INSTALLED:
 306             return PCMK_OCF_NOT_INSTALLED;
 307 
 308         case PCMK_LSB_STATUS_INSUFFICIENT_PRIV:
 309             return PCMK_OCF_INSUFFICIENT_PRIV;
 310 
 311         case PCMK_LSB_STATUS_VAR_PID:
 312         case PCMK_LSB_STATUS_VAR_LOCK:
 313         case PCMK_LSB_STATUS_NOT_RUNNING:
 314             return PCMK_OCF_NOT_RUNNING;
 315 
 316         default:
 317             return PCMK_OCF_UNKNOWN_ERROR;
 318     }
 319 }

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