root/tools/crm_verify.c

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

DEFINITIONS

This source file includes following definitions.
  1. build_arg_context
  2. G_GNUC_PRINTF
  3. G_GNUC_PRINTF
  4. main

   1 /*
   2  * Copyright 2004-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 General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 #include <crm/crm.h>
  12 #include <crm/common/cmdline_internal.h>
  13 #include <crm/common/output_internal.h>
  14 
  15 #include <stdio.h>
  16 #include <sys/types.h>
  17 #include <unistd.h>
  18 
  19 #include <stdlib.h>
  20 #include <errno.h>
  21 #include <fcntl.h>
  22 #include <libgen.h>
  23 #include <glib.h>
  24 
  25 #include <crm/common/xml.h>
  26 #include <crm/common/util.h>
  27 #include <crm/cib.h>
  28 #include <crm/cib/internal.h>
  29 #include <crm/pengine/status.h>
  30 #include <pacemaker-internal.h>
  31 
  32 const char *SUMMARY = "Check a Pacemaker configuration for errors\n\n"
  33                       "Check the well-formedness of a complete Pacemaker XML configuration,\n"
  34                       "its conformance to the configured schema, and the presence of common\n"
  35                       "misconfigurations. Problems reported as errors must be fixed before the\n"
  36                       "cluster will work properly. It is left to the administrator to decide\n"
  37                       "whether to fix problems reported as warnings.";
  38 
  39 struct {
  40     char *cib_save;
  41     gboolean use_live_cib;
  42     char *xml_file;
  43     gboolean xml_stdin;
  44     char *xml_string;
  45     unsigned int verbosity;
  46 } options;
  47 
  48 static GOptionEntry data_entries[] = {
  49     { "live-check", 'L', 0, G_OPTION_ARG_NONE,
  50       &options.use_live_cib, "Check the configuration used by the running cluster",
  51       NULL },
  52     { "xml-file", 'x', 0, G_OPTION_ARG_FILENAME,
  53       &options.xml_file, "Check the configuration in the named file",
  54       "FILE" },
  55     { "xml-pipe", 'p', 0, G_OPTION_ARG_NONE,
  56       &options.xml_stdin, "Check the configuration piped in via stdin",
  57       NULL },
  58     { "xml-text", 'X', 0, G_OPTION_ARG_STRING,
  59       &options.xml_string, "Check the configuration in the supplied string",
  60       "XML" },
  61 
  62     { NULL }
  63 };
  64 
  65 static GOptionEntry addl_entries[] = {
  66     { "save-xml", 'S', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME,
  67       &options.cib_save, "Save verified XML to named file (most useful with -L)",
  68       "FILE" },
  69 
  70     { NULL }
  71 };
  72 
  73 static pcmk__supported_format_t formats[] = {
  74     PCMK__SUPPORTED_FORMAT_NONE,
  75     PCMK__SUPPORTED_FORMAT_TEXT,
  76     PCMK__SUPPORTED_FORMAT_XML,
  77     { NULL, NULL, NULL }
  78 };
  79 
  80 static GOptionContext *
  81 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
     /* [previous][next][first][last][top][bottom][index][help] */
  82     GOptionContext *context = NULL;
  83 
  84     const char *description = "Examples:\n\n"
  85                               "Check the consistency of the configuration in the running cluster:\n\n"
  86                               "\tcrm_verify --live-check\n\n"
  87                               "Check the consistency of the configuration in a given file and "
  88                               "produce quiet output:\n\n"
  89                               "\tcrm_verify --xml-file file.xml --quiet\n\n"
  90                               "Check the consistency of the configuration in a given file and "
  91                               "produce verbose output:\n\n"
  92                               "\tcrm_verify --xml-file file.xml --verbose\n\n";
  93 
  94     GOptionEntry extra_prog_entries[] = {
  95         { "quiet", 'q', 0, G_OPTION_ARG_NONE, &(args->quiet),
  96           "Don't print verify information",
  97           NULL },
  98         { NULL }
  99     };
 100 
 101     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
 102 
 103     pcmk__add_main_args(context, extra_prog_entries);
 104 
 105     g_option_context_set_description(context, description);
 106 
 107     pcmk__add_arg_group(context, "data", "Data sources:",
 108                         "Show data options", data_entries);
 109     pcmk__add_arg_group(context, "additional", "Additional options:",
 110                         "Show additional options", addl_entries);
 111 
 112     return context;
 113 }
 114 
 115 /*!
 116  * \internal
 117  * \brief Output a configuration error
 118  *
 119  * \param[in] ctx  Output object
 120  * \param[in] msg  printf(3)-style format string
 121  * \param[in] ...  Format string arguments
 122  */
 123 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 124 static void
 125 output_config_error(void *ctx, const char *msg, ...)
 126 {
 127     va_list ap;
 128     char *buf = NULL;
 129     pcmk__output_t *out = ctx;
 130 
 131     va_start(ap, msg);
 132     pcmk__assert(vasprintf(&buf, msg, ap) > 0);
 133     if (options.verbosity > 0) {
 134         out->err(out, "error: %s", buf);
 135     }
 136     va_end(ap);
 137 }
 138 
 139 /*!
 140  * \internal
 141  * \brief Output a configuration warning
 142  *
 143  * \param[in] ctx  Output object
 144  * \param[in] msg  printf(3)-style format string
 145  * \param[in] ...  Format string arguments
 146  */
 147 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 148 static void
 149 output_config_warning(void *ctx, const char *msg, ...)
 150 {
 151     va_list ap;
 152     char *buf = NULL;
 153     pcmk__output_t *out = ctx;
 154 
 155     va_start(ap, msg);
 156     pcmk__assert(vasprintf(&buf, msg, ap) > 0);
 157     if (options.verbosity > 0) {
 158         out->err(out, "warning: %s", buf);
 159     }
 160     va_end(ap);
 161 }
 162 
 163 int
 164 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 165 {
 166 
 167     pcmk_scheduler_t *scheduler = NULL;
 168 
 169     int rc = pcmk_rc_ok;
 170     crm_exit_t exit_code = CRM_EX_OK;
 171 
 172     GError *error = NULL;
 173 
 174     pcmk__output_t *out = NULL;
 175 
 176     const char *cib_source = NULL;
 177     xmlNode *cib_object = NULL;
 178 
 179     GOptionGroup *output_group = NULL;
 180 
 181     const char *failure_type = NULL;
 182 
 183     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
 184     gchar **processed_args = pcmk__cmdline_preproc(argv, "xSX");
 185     GOptionContext *context = build_arg_context(args, &output_group);
 186 
 187     pcmk__register_formats(output_group, formats);
 188     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 189         exit_code = CRM_EX_USAGE;
 190         goto done;
 191     }
 192 
 193     if (args->verbosity > 0) {
 194         args->verbosity -= args->quiet;
 195     }
 196 
 197     pcmk__cli_init_logging("crm_verify", args->verbosity);
 198 
 199     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
 200     if (rc != pcmk_rc_ok) {
 201         exit_code = CRM_EX_ERROR;
 202         g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
 203                     args->output_ty, pcmk_rc_str(rc));
 204         goto done;
 205     }
 206 
 207     if (args->version) {
 208         out->version(out, false);
 209         goto done;
 210     }
 211 
 212     pcmk__register_lib_messages(out);
 213 
 214     pcmk__set_config_error_handler(output_config_error, out);
 215     pcmk__set_config_warning_handler(output_config_warning, out);
 216 
 217     if (pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) {
 218         args->verbosity = 1;
 219     }
 220     options.verbosity = args->verbosity;
 221 
 222     if (options.xml_file != NULL) {
 223         cib_source = options.xml_file;
 224     } else if (options.xml_string != NULL) {
 225         cib_source = options.xml_string;
 226     } else if (options.xml_stdin) {
 227         cib_source = "-";
 228     } else if (options.use_live_cib) {
 229         cib_source = NULL;
 230     } else {
 231         rc = ENODATA;
 232         g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "No input specified");
 233         goto done;
 234     }
 235 
 236     rc = pcmk__parse_cib(out, cib_source, &cib_object);
 237 
 238     if (rc != pcmk_rc_ok) {
 239         g_set_error(&error, PCMK__EXITC_ERROR, rc, "Couldn't parse input");
 240         goto done;
 241     }
 242 
 243     if (options.cib_save != NULL) {
 244         pcmk__xml_write_file(cib_object, options.cib_save, false, NULL);
 245     }
 246 
 247     scheduler = pe_new_working_set();
 248 
 249     if (scheduler == NULL) {
 250         rc = errno;
 251         g_set_error(&error, PCMK__RC_ERROR, rc,
 252                     "Could not allocate scheduler data: %s", pcmk_rc_str(rc));
 253         goto done;
 254     }
 255 
 256     scheduler->priv = out;
 257 
 258     rc = pcmk__verify(scheduler, out, &cib_object);
 259 
 260     if (rc == pcmk_rc_schema_validation) {
 261         if (crm_config_error) {
 262             failure_type = "Errors found during check: ";
 263           } else if (crm_config_warning) {
 264             failure_type = "Warnings found during check: ";
 265           } else {
 266             failure_type = "";
 267           }
 268 
 269           if (args->quiet) {
 270               // User requested no output
 271 
 272           } else if (options.verbosity > 0) {
 273               out->err(out, "%sconfig not valid", failure_type);
 274 
 275           } else {
 276               out->err(out, "%sconfig not valid\n-V may provide more details", failure_type);
 277           }
 278         }
 279 
 280     pe_free_working_set(scheduler);
 281 
 282   done:
 283     g_strfreev(processed_args);
 284     pcmk__free_arg_context(context);
 285     free(options.cib_save);
 286     free(options.xml_file);
 287     free(options.xml_string);
 288 
 289     if (cib_object != NULL) {
 290         free_xml(cib_object);      
 291     }
 292 
 293     if (exit_code == CRM_EX_OK) {
 294         exit_code = pcmk_rc2exitc(rc);
 295     }
 296 
 297     pcmk__output_and_clear_error(&error, out);
 298 
 299     if (out != NULL) {
 300         out->finish(out, exit_code, true, NULL);
 301         pcmk__output_free(out);
 302     }
 303 
 304     pcmk__unregister_formats();
 305     crm_exit(exit_code);
 306 }

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