root/lib/common/schemas.c

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

DEFINITIONS

This source file includes following definitions.
  1. xml_log
  2. xml_latest_schema_index
  3. xml_minimum_schema_index
  4. xml_latest_schema
  5. get_schema_root
  6. get_schema_path
  7. version_from_filename
  8. schema_filter
  9. schema_sort
  10. add_schema
  11. crm_schema_init
  12. validate_with_dtd
  13. relaxng_invalid_stderr
  14. validate_with_relaxng
  15. crm_schema_cleanup
  16. validate_with
  17. dump_file
  18. validate_xml_verbose
  19. validate_xml
  20. cib_upgrade_err
  21. apply_transformation
  22. get_schema_name
  23. get_schema_version
  24. update_validation
  25. cli_config_update

   1 /*
   2  * Copyright (C) 2004-2016 Andrew Beekhof <andrew@beekhof.net>
   3  *
   4  * This library is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2.1 of the License, or (at your option) any later version.
   8  *
   9  * This library is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with this library; if not, write to the Free Software
  16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17  */
  18 
  19 #include <crm_internal.h>
  20 
  21 #include <stdio.h>
  22 #include <string.h>
  23 #include <dirent.h>
  24 #include <errno.h>
  25 #include <sys/stat.h>
  26 #include <stdarg.h>
  27 
  28 #if HAVE_LIBXML2
  29 #  include <libxml/relaxng.h>
  30 #endif
  31 
  32 #if HAVE_LIBXSLT
  33 #  include <libxslt/xslt.h>
  34 #  include <libxslt/transform.h>
  35 #  include <libxslt/xsltutils.h>
  36 #endif
  37 
  38 #include <crm/msg_xml.h>
  39 #include <crm/common/xml.h>
  40 #include <crm/common/xml_internal.h>  /* CRM_XML_LOG_BASE */
  41 
  42 typedef struct {
  43     unsigned char v[2];
  44 } schema_version_t;
  45 
  46 #define SCHEMA_ZERO { .v = { 0, 0 } }
  47 
  48 #define schema_scanf(s, prefix, version, suffix) \
  49     sscanf((s), prefix "%hhu.%hhu" suffix, &((version).v[0]), &((version).v[1]))
  50 
  51 #define schema_strdup_printf(prefix, version, suffix) \
  52     crm_strdup_printf(prefix "%u.%u" suffix, (version).v[0], (version).v[1])
  53 
  54 typedef struct {
  55     xmlRelaxNGPtr rng;
  56     xmlRelaxNGValidCtxtPtr valid;
  57     xmlRelaxNGParserCtxtPtr parser;
  58 } relaxng_ctx_cache_t;
  59 
  60 enum schema_validator_e {
  61     schema_validator_none,
  62     schema_validator_dtd,
  63     schema_validator_rng
  64 };
  65 
  66 struct schema_s {
  67     char *name;
  68     char *location;
  69     char *transform;
  70     void *cache;
  71     enum schema_validator_e validator;
  72     int after_transform;
  73     schema_version_t version;
  74 };
  75 
  76 static struct schema_s *known_schemas = NULL;
  77 static int xml_schema_max = 0;
  78 
  79 static void
  80 xml_log(int priority, const char *fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
  81 G_GNUC_PRINTF(2, 3);
  82 
  83 static void
  84 xml_log(int priority, const char *fmt, ...)
  85 {
  86     va_list ap;
  87 
  88     va_start(ap, fmt);
  89     /* XXX should not this enable dechunking as well? */
  90     CRM_XML_LOG_BASE(priority, FALSE, 0, NULL, fmt, ap);
  91     va_end(ap);
  92 }
  93 
  94 static int
  95 xml_latest_schema_index(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  96 {
  97     return xml_schema_max - 4;
  98 }
  99 
 100 static int
 101 xml_minimum_schema_index(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 102 {
 103     static int best = 0;
 104     if (best == 0) {
 105         int lpc = 0;
 106 
 107         best = xml_latest_schema_index();
 108         for (lpc = best; lpc > 0; lpc--) {
 109             if (known_schemas[lpc].version.v[0]
 110                 < known_schemas[best].version.v[0]) {
 111                 return best;
 112             } else {
 113                 best = lpc;
 114             }
 115         }
 116         best = xml_latest_schema_index();
 117     }
 118     return best;
 119 }
 120 
 121 const char *
 122 xml_latest_schema(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 123 {
 124     return get_schema_name(xml_latest_schema_index());
 125 }
 126 
 127 static const char *
 128 get_schema_root(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 129 {
 130     static const char *base = NULL;
 131 
 132     if (base == NULL) {
 133         base = getenv("PCMK_schema_directory");
 134     }
 135     if (base == NULL || strlen(base) == 0) {
 136         base = CRM_DTD_DIRECTORY;
 137     }
 138     return base;
 139 }
 140 
 141 static char *
 142 get_schema_path(const char *name, const char *file)
     /* [previous][next][first][last][top][bottom][index][help] */
 143 {
 144     const char *base = get_schema_root();
 145 
 146     if (file) {
 147         return crm_strdup_printf("%s/%s", base, file);
 148     }
 149     return crm_strdup_printf("%s/%s.rng", base, name);
 150 }
 151 
 152 static inline bool
 153 version_from_filename(const char *filename, schema_version_t *version)
     /* [previous][next][first][last][top][bottom][index][help] */
 154 {
 155     int rc = schema_scanf(filename, "pacemaker-", *version, ".rng");
 156 
 157     return (rc == 2);
 158 }
 159 
 160 static int
 161 schema_filter(const struct dirent *a)
     /* [previous][next][first][last][top][bottom][index][help] */
 162 {
 163     int rc = 0;
 164     schema_version_t version = SCHEMA_ZERO;
 165 
 166     if (strstr(a->d_name, "pacemaker-") != a->d_name) {
 167         /* crm_trace("%s - wrong prefix", a->d_name); */
 168 
 169     } else if (!crm_ends_with_ext(a->d_name, ".rng")) {
 170         /* crm_trace("%s - wrong suffix", a->d_name); */
 171 
 172     } else if (!version_from_filename(a->d_name, &version)) {
 173         /* crm_trace("%s - wrong format", a->d_name); */
 174 
 175     } else if (strcmp(a->d_name, "pacemaker-1.1.rng") == 0) {
 176         /* "-1.1" was used for what later became "-next" */
 177         /* crm_trace("%s - hack", a->d_name); */
 178 
 179     } else {
 180         /* crm_debug("%s - candidate", a->d_name); */
 181         rc = 1;
 182     }
 183 
 184     return rc;
 185 }
 186 
 187 static int
 188 schema_sort(const struct dirent **a, const struct dirent **b)
     /* [previous][next][first][last][top][bottom][index][help] */
 189 {
 190     schema_version_t a_version = SCHEMA_ZERO;
 191     schema_version_t b_version = SCHEMA_ZERO;
 192 
 193     if (!version_from_filename(a[0]->d_name, &a_version)
 194         || !version_from_filename(b[0]->d_name, &b_version)) {
 195         // Shouldn't be possible, but makes static analysis happy
 196         return 0;
 197     }
 198 
 199     for (int i = 0; i < 2; ++i) {
 200         if (a_version.v[i] < b_version.v[i]) {
 201             return -1;
 202         } else if (a_version.v[i] > b_version.v[i]) {
 203             return 1;
 204         }
 205     }
 206     return 0;
 207 }
 208 
 209 static void
 210 add_schema(enum schema_validator_e validator, const schema_version_t *version,
     /* [previous][next][first][last][top][bottom][index][help] */
 211            const char *name, const char *location, const char *transform,
 212            int after_transform)
 213 {
 214     int last = xml_schema_max;
 215     bool have_version = FALSE;
 216 
 217     xml_schema_max++;
 218     known_schemas = realloc_safe(known_schemas,
 219                                  xml_schema_max * sizeof(struct schema_s));
 220     CRM_ASSERT(known_schemas != NULL);
 221     memset(known_schemas+last, 0, sizeof(struct schema_s));
 222     known_schemas[last].validator = validator;
 223     known_schemas[last].after_transform = after_transform;
 224 
 225     for (int i = 0; i < 2; ++i) {
 226         known_schemas[last].version.v[i] = version->v[i];
 227         if (version->v[i]) {
 228             have_version = TRUE;
 229         }
 230     }
 231     if (have_version) {
 232         known_schemas[last].name = schema_strdup_printf("pacemaker-", *version, "");
 233         known_schemas[last].location = crm_strdup_printf("%s.rng",
 234                                                          known_schemas[last].name);
 235     } else {
 236         CRM_ASSERT(name);
 237         CRM_ASSERT(location);
 238         schema_scanf(name, "%*[^-]-", known_schemas[last].version, "");
 239         known_schemas[last].name = strdup(name);
 240         known_schemas[last].location = strdup(location);
 241     }
 242 
 243     if (transform) {
 244         known_schemas[last].transform = strdup(transform);
 245     }
 246     if (after_transform == 0) {
 247         after_transform = xml_schema_max;  /* upgrade is a one-way */
 248     }
 249     known_schemas[last].after_transform = after_transform;
 250 
 251     if (known_schemas[last].after_transform < 0) {
 252         crm_debug("Added supported schema %d: %s (%s)",
 253                   last, known_schemas[last].name, known_schemas[last].location);
 254 
 255     } else if (known_schemas[last].transform) {
 256         crm_debug("Added supported schema %d: %s (%s upgrades to %d with %s)",
 257                   last, known_schemas[last].name, known_schemas[last].location,
 258                   known_schemas[last].after_transform,
 259                   known_schemas[last].transform);
 260 
 261     } else {
 262         crm_debug("Added supported schema %d: %s (%s upgrades to %d)",
 263                   last, known_schemas[last].name, known_schemas[last].location,
 264                   known_schemas[last].after_transform);
 265     }
 266 }
 267 
 268 /*!
 269  * \internal
 270  * \brief Load pacemaker schemas into cache
 271  */
 272 void
 273 crm_schema_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 274 {
 275     int lpc, max;
 276     const char *base = get_schema_root();
 277     struct dirent **namelist = NULL;
 278     const schema_version_t zero = SCHEMA_ZERO;
 279 
 280     max = scandir(base, &namelist, schema_filter, schema_sort);
 281 
 282     add_schema(schema_validator_dtd, &zero, "pacemaker-0.6",
 283                "crm.dtd", "upgrade06.xsl", 3);
 284 
 285     add_schema(schema_validator_dtd, &zero, "transitional-0.6",
 286                "crm-transitional.dtd", "upgrade06.xsl", 3);
 287 
 288     add_schema(schema_validator_rng, &zero, "pacemaker-0.7",
 289                "pacemaker-1.0.rng", NULL, 0);
 290 
 291     if (max < 0) {
 292         crm_notice("scandir(%s) failed: %s (%d)", base, strerror(errno), errno);
 293 
 294     } else {
 295         for (lpc = 0; lpc < max; lpc++) {
 296             int next = 0;
 297             schema_version_t version = SCHEMA_ZERO;
 298             char *transform = NULL;
 299 
 300             if (!version_from_filename(namelist[lpc]->d_name, &version)) {
 301                 // Shouldn't be possible, but makes static analysis happy
 302                 crm_err("Skipping schema '%s': could not parse version",
 303                         namelist[lpc]->d_name);
 304                 free(namelist[lpc]);
 305                 continue;
 306             }
 307             if ((lpc + 1) < max) {
 308                 schema_version_t next_version = SCHEMA_ZERO;
 309 
 310                 if (version_from_filename(namelist[lpc+1]->d_name, &next_version)
 311                     && (version.v[0] < next_version.v[0])) {
 312 
 313                     struct stat s;
 314                     char *xslt = NULL;
 315 
 316                     transform = schema_strdup_printf("upgrade-", version, ".xsl");
 317                     xslt = get_schema_path(NULL, transform);
 318                     if (stat(xslt, &s) != 0) {
 319                         crm_err("Transform %s not found", xslt);
 320                         free(xslt);
 321                         add_schema(schema_validator_rng, &version, NULL, NULL,
 322                                    NULL, -1);
 323                         break;
 324                     } else {
 325                         free(xslt);
 326                     }
 327                 }
 328 
 329             } else {
 330                 next = -1;
 331             }
 332             add_schema(schema_validator_rng, &version, NULL, NULL, transform,
 333                        next);
 334             free(namelist[lpc]);
 335             free(transform);
 336         }
 337     }
 338 
 339     /* 1.1 was the old name for -next */
 340     add_schema(schema_validator_rng, &zero, "pacemaker-1.1",
 341                "pacemaker-next.rng", NULL, 0);
 342 
 343     add_schema(schema_validator_rng, &zero, "pacemaker-next",
 344                "pacemaker-next.rng", NULL, -1);
 345 
 346     add_schema(schema_validator_none, &zero, "none",
 347                "N/A", NULL, -1);
 348     free(namelist);
 349 }
 350 
 351 static gboolean
 352 validate_with_dtd(xmlDocPtr doc, gboolean to_logs, const char *dtd_file)
     /* [previous][next][first][last][top][bottom][index][help] */
 353 {
 354     gboolean valid = TRUE;
 355 
 356     xmlDtdPtr dtd = NULL;
 357     xmlValidCtxtPtr cvp = NULL;
 358 
 359     CRM_CHECK(doc != NULL, return FALSE);
 360     CRM_CHECK(dtd_file != NULL, return FALSE);
 361 
 362     dtd = xmlParseDTD(NULL, (const xmlChar *)dtd_file);
 363     if (dtd == NULL) {
 364         crm_err("Could not locate/parse DTD: %s", dtd_file);
 365         return TRUE;
 366     }
 367 
 368     cvp = xmlNewValidCtxt();
 369     if (cvp) {
 370         if (to_logs) {
 371             cvp->userData = (void *)LOG_ERR;
 372             cvp->error = (xmlValidityErrorFunc) xml_log;
 373             cvp->warning = (xmlValidityWarningFunc) xml_log;
 374         } else {
 375             cvp->userData = (void *)stderr;
 376             cvp->error = (xmlValidityErrorFunc) fprintf;
 377             cvp->warning = (xmlValidityWarningFunc) fprintf;
 378         }
 379 
 380         if (!xmlValidateDtd(cvp, doc, dtd)) {
 381             valid = FALSE;
 382         }
 383         xmlFreeValidCtxt(cvp);
 384 
 385     } else {
 386         crm_err("Internal error: No valid context");
 387     }
 388 
 389     xmlFreeDtd(dtd);
 390     return valid;
 391 }
 392 
 393 #if 0
 394 static void
 395 relaxng_invalid_stderr(void *userData, xmlErrorPtr error)
     /* [previous][next][first][last][top][bottom][index][help] */
 396 {
 397     /*
 398        Structure xmlError
 399        struct _xmlError {
 400        int      domain  : What part of the library raised this er
 401        int      code    : The error code, e.g. an xmlParserError
 402        char *   message : human-readable informative error messag
 403        xmlErrorLevel    level   : how consequent is the error
 404        char *   file    : the filename
 405        int      line    : the line number if available
 406        char *   str1    : extra string information
 407        char *   str2    : extra string information
 408        char *   str3    : extra string information
 409        int      int1    : extra number information
 410        int      int2    : column number of the error or 0 if N/A
 411        void *   ctxt    : the parser context if available
 412        void *   node    : the node in the tree
 413        }
 414      */
 415     crm_err("Structured error: line=%d, level=%d %s", error->line, error->level, error->message);
 416 }
 417 #endif
 418 
 419 static gboolean
 420 validate_with_relaxng(xmlDocPtr doc, gboolean to_logs, const char *relaxng_file,
     /* [previous][next][first][last][top][bottom][index][help] */
 421                       relaxng_ctx_cache_t **cached_ctx)
 422 {
 423     int rc = 0;
 424     gboolean valid = TRUE;
 425     relaxng_ctx_cache_t *ctx = NULL;
 426 
 427     CRM_CHECK(doc != NULL, return FALSE);
 428     CRM_CHECK(relaxng_file != NULL, return FALSE);
 429 
 430     if (cached_ctx && *cached_ctx) {
 431         ctx = *cached_ctx;
 432 
 433     } else {
 434         crm_info("Creating RNG parser context");
 435         ctx = calloc(1, sizeof(relaxng_ctx_cache_t));
 436 
 437         xmlLoadExtDtdDefaultValue = 1;
 438         ctx->parser = xmlRelaxNGNewParserCtxt(relaxng_file);
 439         CRM_CHECK(ctx->parser != NULL, goto cleanup);
 440 
 441         if (to_logs) {
 442             xmlRelaxNGSetParserErrors(ctx->parser,
 443                                       (xmlRelaxNGValidityErrorFunc) xml_log,
 444                                       (xmlRelaxNGValidityWarningFunc) xml_log,
 445                                       GUINT_TO_POINTER(LOG_ERR));
 446         } else {
 447             xmlRelaxNGSetParserErrors(ctx->parser,
 448                                       (xmlRelaxNGValidityErrorFunc) fprintf,
 449                                       (xmlRelaxNGValidityWarningFunc) fprintf,
 450                                       stderr);
 451         }
 452 
 453         ctx->rng = xmlRelaxNGParse(ctx->parser);
 454         CRM_CHECK(ctx->rng != NULL,
 455                   crm_err("Could not find/parse %s", relaxng_file);
 456                   goto cleanup);
 457 
 458         ctx->valid = xmlRelaxNGNewValidCtxt(ctx->rng);
 459         CRM_CHECK(ctx->valid != NULL, goto cleanup);
 460 
 461         if (to_logs) {
 462             xmlRelaxNGSetValidErrors(ctx->valid,
 463                                      (xmlRelaxNGValidityErrorFunc) xml_log,
 464                                      (xmlRelaxNGValidityWarningFunc) xml_log,
 465                                      GUINT_TO_POINTER(LOG_ERR));
 466         } else {
 467             xmlRelaxNGSetValidErrors(ctx->valid,
 468                                      (xmlRelaxNGValidityErrorFunc) fprintf,
 469                                      (xmlRelaxNGValidityWarningFunc) fprintf,
 470                                      stderr);
 471         }
 472     }
 473 
 474     /* xmlRelaxNGSetValidStructuredErrors( */
 475     /*  valid, relaxng_invalid_stderr, valid); */
 476 
 477     xmlLineNumbersDefault(1);
 478     rc = xmlRelaxNGValidateDoc(ctx->valid, doc);
 479     if (rc > 0) {
 480         valid = FALSE;
 481 
 482     } else if (rc < 0) {
 483         crm_err("Internal libxml error during validation");
 484     }
 485 
 486   cleanup:
 487 
 488     if (cached_ctx) {
 489         *cached_ctx = ctx;
 490 
 491     } else {
 492         if (ctx->parser != NULL) {
 493             xmlRelaxNGFreeParserCtxt(ctx->parser);
 494         }
 495         if (ctx->valid != NULL) {
 496             xmlRelaxNGFreeValidCtxt(ctx->valid);
 497         }
 498         if (ctx->rng != NULL) {
 499             xmlRelaxNGFree(ctx->rng);
 500         }
 501         free(ctx);
 502     }
 503 
 504     return valid;
 505 }
 506 
 507 /*!
 508  * \internal
 509  * \brief Clean up global memory associated with XML schemas
 510  */
 511 void
 512 crm_schema_cleanup(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 513 {
 514     int lpc;
 515     relaxng_ctx_cache_t *ctx = NULL;
 516 
 517     for (lpc = 0; lpc < xml_schema_max; lpc++) {
 518 
 519         switch (known_schemas[lpc].validator) {
 520             case schema_validator_none:
 521             case schema_validator_dtd: // not cached
 522                 break;
 523             case schema_validator_rng: // cached
 524                 ctx = (relaxng_ctx_cache_t *) known_schemas[lpc].cache;
 525                 if (ctx == NULL) {
 526                     break;
 527                 }
 528                 if (ctx->parser != NULL) {
 529                     xmlRelaxNGFreeParserCtxt(ctx->parser);
 530                 }
 531                 if (ctx->valid != NULL) {
 532                     xmlRelaxNGFreeValidCtxt(ctx->valid);
 533                 }
 534                 if (ctx->rng != NULL) {
 535                     xmlRelaxNGFree(ctx->rng);
 536                 }
 537                 free(ctx);
 538                 known_schemas[lpc].cache = NULL;
 539                 break;
 540         }
 541         free(known_schemas[lpc].name);
 542         free(known_schemas[lpc].location);
 543         free(known_schemas[lpc].transform);
 544     }
 545     free(known_schemas);
 546     known_schemas = NULL;
 547 }
 548 
 549 static gboolean
 550 validate_with(xmlNode *xml, int method, gboolean to_logs)
     /* [previous][next][first][last][top][bottom][index][help] */
 551 {
 552     xmlDocPtr doc = NULL;
 553     gboolean valid = FALSE;
 554     char *file = NULL;
 555 
 556     if (method < 0) {
 557         return FALSE;
 558     }
 559 
 560     if (known_schemas[method].validator == schema_validator_none) {
 561         return TRUE;
 562     }
 563 
 564     CRM_CHECK(xml != NULL, return FALSE);
 565     doc = getDocPtr(xml);
 566     file = get_schema_path(known_schemas[method].name,
 567                            known_schemas[method].location);
 568 
 569     crm_trace("Validating with: %s (type=%d)",
 570               crm_str(file), known_schemas[method].validator);
 571     switch (known_schemas[method].validator) {
 572         case schema_validator_dtd:
 573             valid = validate_with_dtd(doc, to_logs, file);
 574             break;
 575         case schema_validator_rng:
 576             valid =
 577                 validate_with_relaxng(doc, to_logs, file,
 578                                       (relaxng_ctx_cache_t **) & (known_schemas[method].cache));
 579             break;
 580         default:
 581             crm_err("Unknown validator type: %d",
 582                     known_schemas[method].validator);
 583             break;
 584     }
 585 
 586     free(file);
 587     return valid;
 588 }
 589 
 590 static void
 591 dump_file(const char *filename)
     /* [previous][next][first][last][top][bottom][index][help] */
 592 {
 593 
 594     FILE *fp = NULL;
 595     int ch, line = 0;
 596 
 597     CRM_CHECK(filename != NULL, return);
 598 
 599     fp = fopen(filename, "r");
 600     if (fp == NULL) {
 601         crm_perror(LOG_ERR, "Could not open %s for reading", filename);
 602         return;
 603     }
 604 
 605     fprintf(stderr, "%4d ", ++line);
 606     do {
 607         ch = getc(fp);
 608         if (ch == EOF) {
 609             putc('\n', stderr);
 610             break;
 611         } else if (ch == '\n') {
 612             fprintf(stderr, "\n%4d ", ++line);
 613         } else {
 614             putc(ch, stderr);
 615         }
 616     } while (1);
 617 
 618     fclose(fp);
 619 }
 620 
 621 gboolean
 622 validate_xml_verbose(xmlNode *xml_blob)
     /* [previous][next][first][last][top][bottom][index][help] */
 623 {
 624     int fd = 0;
 625     xmlDoc *doc = NULL;
 626     xmlNode *xml = NULL;
 627     gboolean rc = FALSE;
 628     char *filename = strdup(CRM_STATE_DIR "/cib-invalid.XXXXXX");
 629 
 630     CRM_CHECK(filename != NULL, return FALSE);
 631 
 632     umask(S_IWGRP | S_IWOTH | S_IROTH);
 633     fd = mkstemp(filename);
 634     write_xml_fd(xml_blob, filename, fd, FALSE);
 635 
 636     dump_file(filename);
 637 
 638     doc = xmlParseFile(filename);
 639     xml = xmlDocGetRootElement(doc);
 640     rc = validate_xml(xml, NULL, FALSE);
 641     free_xml(xml);
 642 
 643     unlink(filename);
 644     free(filename);
 645 
 646     return rc;
 647 }
 648 
 649 gboolean
 650 validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
     /* [previous][next][first][last][top][bottom][index][help] */
 651 {
 652     int version = 0;
 653 
 654     if (validation == NULL) {
 655         validation = crm_element_value(xml_blob, XML_ATTR_VALIDATION);
 656     }
 657 
 658     if (validation == NULL) {
 659         int lpc = 0;
 660         bool valid = FALSE;
 661 
 662         /* @COMPAT pre-1.0 configs */
 663         validation = crm_element_value(xml_blob, "ignore_dtd");
 664         if (crm_is_true(validation)) {
 665             crm_xml_add(xml_blob, XML_ATTR_VALIDATION, "none");
 666             return TRUE;
 667         }
 668 
 669         /* Work it out */
 670         for (lpc = 0; lpc < xml_schema_max; lpc++) {
 671             if (validate_with(xml_blob, lpc, FALSE)) {
 672                 valid = TRUE;
 673                 crm_xml_add(xml_blob, XML_ATTR_VALIDATION,
 674                             known_schemas[lpc].name);
 675                 crm_info("XML validated against %s", known_schemas[lpc].name);
 676                 if(known_schemas[lpc].after_transform == 0) {
 677                     break;
 678                 }
 679             }
 680         }
 681 
 682         return valid;
 683     }
 684 
 685     version = get_schema_version(validation);
 686     if (strcmp(validation, "none") == 0) {
 687         return TRUE;
 688     } else if (version < xml_schema_max) {
 689         return validate_with(xml_blob, version, to_logs);
 690     }
 691 
 692     crm_err("Unknown validator: %s", validation);
 693     return FALSE;
 694 }
 695 
 696 #if HAVE_LIBXSLT
 697 
 698 static void
 699 cib_upgrade_err(void *ctx, const char *fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
 700 G_GNUC_PRINTF(2, 3);
 701 
 702 static void
 703 cib_upgrade_err(void *ctx, const char *fmt, ...)
 704 {
 705     va_list ap;
 706 
 707     va_start(ap, fmt);
 708     CRM_XML_LOG_BASE(LOG_WARNING, TRUE, 0, "CIB upgrade: ", fmt, ap);
 709     va_end(ap);
 710 }
 711 
 712 static xmlNode *
 713 apply_transformation(xmlNode *xml, const char *transform, gboolean to_logs)
     /* [previous][next][first][last][top][bottom][index][help] */
 714 {
 715     char *xform = NULL;
 716     xmlNode *out = NULL;
 717     xmlDocPtr res = NULL;
 718     xmlDocPtr doc = NULL;
 719     xsltStylesheet *xslt = NULL;
 720 
 721     CRM_CHECK(xml != NULL, return FALSE);
 722     doc = getDocPtr(xml);
 723     xform = get_schema_path(NULL, transform);
 724 
 725     xmlLoadExtDtdDefaultValue = 1;
 726     xmlSubstituteEntitiesDefault(1);
 727 
 728     /* for capturing, e.g., what's emitted via <xsl:message> */
 729     if (to_logs) {
 730         xsltSetGenericErrorFunc(NULL, cib_upgrade_err);
 731     } else {
 732         xsltSetGenericErrorFunc((void *) stderr, (xmlGenericErrorFunc) fprintf);
 733     }
 734 
 735     xslt = xsltParseStylesheetFile((const xmlChar *)xform);
 736     CRM_CHECK(xslt != NULL, goto cleanup);
 737 
 738     res = xsltApplyStylesheet(xslt, doc, NULL);
 739     CRM_CHECK(res != NULL, goto cleanup);
 740 
 741     xsltSetGenericErrorFunc(NULL, NULL);  /* restore default one */
 742 
 743     out = xmlDocGetRootElement(res);
 744 
 745   cleanup:
 746     if (xslt) {
 747         xsltFreeStylesheet(xslt);
 748     }
 749 
 750     free(xform);
 751 
 752     return out;
 753 }
 754 #endif
 755 
 756 const char *
 757 get_schema_name(int version)
     /* [previous][next][first][last][top][bottom][index][help] */
 758 {
 759     if (version < 0 || version >= xml_schema_max) {
 760         return "unknown";
 761     }
 762     return known_schemas[version].name;
 763 }
 764 
 765 int
 766 get_schema_version(const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 767 {
 768     int lpc = 0;
 769 
 770     if (name == NULL) {
 771         name = "none";
 772     }
 773     for (; lpc < xml_schema_max; lpc++) {
 774         if (safe_str_eq(name, known_schemas[lpc].name)) {
 775             return lpc;
 776         }
 777     }
 778     return -1;
 779 }
 780 
 781 /* set which validation to use */
 782 int
 783 update_validation(xmlNode **xml_blob, int *best, int max, gboolean transform,
     /* [previous][next][first][last][top][bottom][index][help] */
 784                   gboolean to_logs)
 785 {
 786     xmlNode *xml = NULL;
 787     char *value = NULL;
 788     int max_stable_schemas = xml_latest_schema_index();
 789     int lpc = 0, match = -1, rc = pcmk_ok;
 790     int next = -1;  /* -1 denotes "inactive" value */
 791 
 792     CRM_CHECK(best != NULL, return -EINVAL);
 793     *best = 0;
 794 
 795     CRM_CHECK(xml_blob != NULL, return -EINVAL);
 796     CRM_CHECK(*xml_blob != NULL, return -EINVAL);
 797 
 798     xml = *xml_blob;
 799     value = crm_element_value_copy(xml, XML_ATTR_VALIDATION);
 800 
 801     if (value != NULL) {
 802         match = get_schema_version(value);
 803 
 804         lpc = match;
 805         if (lpc >= 0 && transform == FALSE) {
 806             *best = lpc++;
 807 
 808         } else if (lpc < 0) {
 809             crm_debug("Unknown validation schema");
 810             lpc = 0;
 811         }
 812     }
 813 
 814     if (match >= max_stable_schemas) {
 815         /* nothing to do */
 816         free(value);
 817         *best = match;
 818         return pcmk_ok;
 819     }
 820 
 821     while (lpc <= max_stable_schemas) {
 822         crm_debug("Testing '%s' validation (%d of %d)",
 823                   known_schemas[lpc].name ? known_schemas[lpc].name : "<unset>",
 824                   lpc, max_stable_schemas);
 825 
 826         if (validate_with(xml, lpc, to_logs) == FALSE) {
 827             if (next != -1) {
 828                 crm_info("Configuration not valid for schema: %s",
 829                          known_schemas[lpc].name);
 830                 next = -1;
 831             } else {
 832                 crm_trace("%s validation failed",
 833                           known_schemas[lpc].name ? known_schemas[lpc].name : "<unset>");
 834             }
 835             if (*best) {
 836                 /* we've satisfied the validation, no need to check further */
 837                 break;
 838             }
 839             rc = -pcmk_err_schema_validation;
 840 
 841         } else {
 842             if (next != -1) {
 843                 crm_debug("Configuration valid for schema: %s",
 844                           known_schemas[next].name);
 845                 next = -1;
 846             }
 847             rc = pcmk_ok;
 848         }
 849 
 850         if (rc == pcmk_ok) {
 851             *best = lpc;
 852         }
 853 
 854         if (rc == pcmk_ok && transform) {
 855             xmlNode *upgrade = NULL;
 856             next = known_schemas[lpc].after_transform;
 857 
 858             if (next <= lpc) {
 859                 /* There is no next version, or next would regress */
 860                 crm_trace("Stopping at %s", known_schemas[lpc].name);
 861                 break;
 862 
 863             } else if (max > 0 && (lpc == max || next > max)) {
 864                 crm_trace("Upgrade limit reached at %s (lpc=%d, next=%d, max=%d)",
 865                           known_schemas[lpc].name, lpc, next, max);
 866                 break;
 867 
 868             } else if (known_schemas[lpc].transform == NULL) {
 869                 crm_debug("%s-style configuration is also valid for %s",
 870                            known_schemas[lpc].name, known_schemas[next].name);
 871 
 872                 lpc = next;
 873 
 874             } else {
 875                 crm_debug("Upgrading %s-style configuration to %s with %s",
 876                            known_schemas[lpc].name, known_schemas[next].name,
 877                            known_schemas[lpc].transform ? known_schemas[lpc].transform : "no-op");
 878 
 879 #if HAVE_LIBXSLT
 880                 upgrade = apply_transformation(xml, known_schemas[lpc].transform, to_logs);
 881 #endif
 882                 if (upgrade == NULL) {
 883                     crm_err("Transformation %s failed",
 884                             known_schemas[lpc].transform);
 885                     rc = -pcmk_err_transform_failed;
 886 
 887                 } else if (validate_with(upgrade, next, to_logs)) {
 888                     crm_info("Transformation %s successful",
 889                              known_schemas[lpc].transform);
 890                     lpc = next;
 891                     *best = next;
 892                     free_xml(xml);
 893                     xml = upgrade;
 894                     rc = pcmk_ok;
 895 
 896                 } else {
 897                     crm_err("Transformation %s did not produce a valid configuration",
 898                             known_schemas[lpc].transform);
 899                     crm_log_xml_info(upgrade, "transform:bad");
 900                     free_xml(upgrade);
 901                     rc = -pcmk_err_schema_validation;
 902                 }
 903                 next = -1;
 904             }
 905         }
 906 
 907         if (transform == FALSE || rc != pcmk_ok) {
 908             /* we need some progress! */
 909             lpc++;
 910         }
 911     }
 912 
 913     if (*best > match && *best) {
 914         crm_info("%s the configuration from %s to %s",
 915                    transform?"Transformed":"Upgraded",
 916                    value ? value : "<none>", known_schemas[*best].name);
 917         crm_xml_add(xml, XML_ATTR_VALIDATION, known_schemas[*best].name);
 918     }
 919 
 920     *xml_blob = xml;
 921     free(value);
 922     return rc;
 923 }
 924 
 925 gboolean
 926 cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs)
     /* [previous][next][first][last][top][bottom][index][help] */
 927 {
 928     gboolean rc = TRUE;
 929     const char *value = crm_element_value(*xml, XML_ATTR_VALIDATION);
 930     char *const orig_value = strdup(value == NULL ? "(none)" : value);
 931 
 932     int version = get_schema_version(value);
 933     int orig_version = version;
 934     int min_version = xml_minimum_schema_index();
 935 
 936     if (version < min_version) {
 937         xmlNode *converted = NULL;
 938 
 939         converted = copy_xml(*xml);
 940         update_validation(&converted, &version, 0, TRUE, to_logs);
 941 
 942         value = crm_element_value(converted, XML_ATTR_VALIDATION);
 943         if (version < min_version) {
 944             if (version < orig_version || orig_version == -1) {
 945                 if (to_logs) {
 946                     crm_config_err("Your current configuration %s could not"
 947                                    " validate with any schema in range [%s, %s],"
 948                                    " cannot upgrade to %s.",
 949                                    orig_value,
 950                                    get_schema_name(orig_version),
 951                                    xml_latest_schema(),
 952                                    get_schema_name(min_version));
 953                 } else {
 954                     fprintf(stderr, "Your current configuration %s could not"
 955                                     " validate with any schema in range [%s, %s],"
 956                                     " cannot upgrade to %s.\n",
 957                                     orig_value,
 958                                     get_schema_name(orig_version),
 959                                     xml_latest_schema(),
 960                                     get_schema_name(min_version));
 961                 }
 962             } else if (to_logs) {
 963                 crm_config_err("Your current configuration could only be upgraded to %s... "
 964                                "the minimum requirement is %s.", crm_str(value),
 965                                get_schema_name(min_version));
 966             } else {
 967                 fprintf(stderr, "Your current configuration could only be upgraded to %s... "
 968                         "the minimum requirement is %s.\n",
 969                         crm_str(value), get_schema_name(min_version));
 970             }
 971 
 972             free_xml(converted);
 973             converted = NULL;
 974             rc = FALSE;
 975 
 976         } else {
 977             free_xml(*xml);
 978             *xml = converted;
 979 
 980             if (version < xml_latest_schema_index()) {
 981                 crm_config_warn("Your configuration was internally updated to %s... "
 982                                 "which is acceptable but not the most recent",
 983                                 get_schema_name(version));
 984 
 985             } else if (to_logs) {
 986                 crm_info("Your configuration was internally updated to the latest version (%s)",
 987                          get_schema_name(version));
 988             }
 989         }
 990 
 991     } else if (version >= get_schema_version("none")) {
 992         if (to_logs) {
 993             crm_config_warn("Configuration validation is currently disabled."
 994                             " It is highly encouraged and prevents many common cluster issues.");
 995 
 996         } else {
 997             fprintf(stderr, "Configuration validation is currently disabled."
 998                     " It is highly encouraged and prevents many common cluster issues.\n");
 999         }
1000     }
1001 
1002     if (best_version) {
1003         *best_version = version;
1004     }
1005 
1006     free(orig_value);
1007     return rc;
1008 }

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