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
  7. services_action_create
  8. services_list

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

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