This source file includes following definitions.
- xml_log
 
- xml_latest_schema_index
 
- xml_minimum_schema_index
 
- xml_latest_schema
 
- version_from_filename
 
- schema_filter
 
- schema_sort
 
- add_schema
 
- add_schema_by_version
 
- wrap_libxslt
 
- crm_schema_init
 
- relaxng_invalid_stderr
 
- validate_with_relaxng
 
- crm_schema_cleanup
 
- validate_with
 
- validate_with_silent
 
- dump_file
 
- validate_xml_verbose
 
- validate_xml
 
- cib_upgrade_err
 
- apply_transformation
 
- apply_upgrade
 
- get_schema_name
 
- get_schema_version
 
- update_validation
 
- cli_config_update
 
   1 
   2 
   3 
   4 
   5 
   6 
   7 
   8 
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdio.h>
  13 #include <string.h>
  14 #include <dirent.h>
  15 #include <errno.h>
  16 #include <sys/stat.h>
  17 #include <stdarg.h>
  18 
  19 #include <libxml/relaxng.h>
  20 
  21 #if HAVE_LIBXSLT
  22 #  include <libxslt/xslt.h>
  23 #  include <libxslt/transform.h>
  24 #  include <libxslt/security.h>
  25 #  include <libxslt/xsltutils.h>
  26 #endif
  27 
  28 #include <crm/msg_xml.h>
  29 #include <crm/common/xml.h>
  30 #include <crm/common/xml_internal.h>  
  31 
  32 typedef struct {
  33     unsigned char v[2];
  34 } schema_version_t;
  35 
  36 #define SCHEMA_ZERO { .v = { 0, 0 } }
  37 
  38 #define schema_scanf(s, prefix, version, suffix) \
  39     sscanf((s), prefix "%hhu.%hhu" suffix, &((version).v[0]), &((version).v[1]))
  40 
  41 #define schema_strdup_printf(prefix, version, suffix) \
  42     crm_strdup_printf(prefix "%u.%u" suffix, (version).v[0], (version).v[1])
  43 
  44 typedef struct {
  45     xmlRelaxNGPtr rng;
  46     xmlRelaxNGValidCtxtPtr valid;
  47     xmlRelaxNGParserCtxtPtr parser;
  48 } relaxng_ctx_cache_t;
  49 
  50 enum schema_validator_e {
  51     schema_validator_none,
  52     schema_validator_rng
  53 };
  54 
  55 struct schema_s {
  56     char *name;
  57     char *transform;
  58     void *cache;
  59     enum schema_validator_e validator;
  60     int after_transform;
  61     schema_version_t version;
  62     char *transform_enter;
  63     bool transform_onleave;
  64 };
  65 
  66 static struct schema_s *known_schemas = NULL;
  67 static int xml_schema_max = 0;
  68 static bool silent_logging = FALSE;
  69 
  70 static void
  71 xml_log(int priority, const char *fmt, ...)
     
  72 G_GNUC_PRINTF(2, 3);
  73 
  74 static void
  75 xml_log(int priority, const char *fmt, ...)
  76 {
  77     va_list ap;
  78 
  79     va_start(ap, fmt);
  80     if (silent_logging == FALSE) {
  81         
  82         PCMK__XML_LOG_BASE(priority, FALSE, 0, NULL, fmt, ap);
  83     }
  84     va_end(ap);
  85 }
  86 
  87 static int
  88 xml_latest_schema_index(void)
     
  89 {
  90     return xml_schema_max - 3; 
  91 }
  92 
  93 static int
  94 xml_minimum_schema_index(void)
     
  95 {
  96     static int best = 0;
  97     if (best == 0) {
  98         int lpc = 0;
  99 
 100         best = xml_latest_schema_index();
 101         for (lpc = best; lpc > 0; lpc--) {
 102             if (known_schemas[lpc].version.v[0]
 103                 < known_schemas[best].version.v[0]) {
 104                 return best;
 105             } else {
 106                 best = lpc;
 107             }
 108         }
 109         best = xml_latest_schema_index();
 110     }
 111     return best;
 112 }
 113 
 114 const char *
 115 xml_latest_schema(void)
     
 116 {
 117     return get_schema_name(xml_latest_schema_index());
 118 }
 119 
 120 static inline bool
 121 version_from_filename(const char *filename, schema_version_t *version)
     
 122 {
 123     int rc = schema_scanf(filename, "pacemaker-", *version, ".rng");
 124 
 125     return (rc == 2);
 126 }
 127 
 128 static int
 129 schema_filter(const struct dirent *a)
     
 130 {
 131     int rc = 0;
 132     schema_version_t version = SCHEMA_ZERO;
 133 
 134     if (strstr(a->d_name, "pacemaker-") != a->d_name) {
 135         
 136 
 137     } else if (!pcmk__ends_with_ext(a->d_name, ".rng")) {
 138         
 139 
 140     } else if (!version_from_filename(a->d_name, &version)) {
 141         
 142 
 143     } else {
 144         
 145         rc = 1;
 146     }
 147 
 148     return rc;
 149 }
 150 
 151 static int
 152 schema_sort(const struct dirent **a, const struct dirent **b)
     
 153 {
 154     schema_version_t a_version = SCHEMA_ZERO;
 155     schema_version_t b_version = SCHEMA_ZERO;
 156 
 157     if (!version_from_filename(a[0]->d_name, &a_version)
 158         || !version_from_filename(b[0]->d_name, &b_version)) {
 159         
 160         return 0;
 161     }
 162 
 163     for (int i = 0; i < 2; ++i) {
 164         if (a_version.v[i] < b_version.v[i]) {
 165             return -1;
 166         } else if (a_version.v[i] > b_version.v[i]) {
 167             return 1;
 168         }
 169     }
 170     return 0;
 171 }
 172 
 173 
 174 
 175 
 176 
 177 
 178 
 179 
 180 static void
 181 add_schema(enum schema_validator_e validator, const schema_version_t *version,
     
 182            const char *name, const char *transform,
 183            const char *transform_enter, bool transform_onleave,
 184            int after_transform)
 185 {
 186     int last = xml_schema_max;
 187     bool have_version = FALSE;
 188 
 189     xml_schema_max++;
 190     known_schemas = pcmk__realloc(known_schemas,
 191                                   xml_schema_max * sizeof(struct schema_s));
 192     CRM_ASSERT(known_schemas != NULL);
 193     memset(known_schemas+last, 0, sizeof(struct schema_s));
 194     known_schemas[last].validator = validator;
 195     known_schemas[last].after_transform = after_transform;
 196 
 197     for (int i = 0; i < 2; ++i) {
 198         known_schemas[last].version.v[i] = version->v[i];
 199         if (version->v[i]) {
 200             have_version = TRUE;
 201         }
 202     }
 203     if (have_version) {
 204         known_schemas[last].name = schema_strdup_printf("pacemaker-", *version, "");
 205     } else {
 206         CRM_ASSERT(name);
 207         schema_scanf(name, "%*[^-]-", known_schemas[last].version, "");
 208         known_schemas[last].name = strdup(name);
 209     }
 210 
 211     if (transform) {
 212         known_schemas[last].transform = strdup(transform);
 213     }
 214     if (transform_enter) {
 215         known_schemas[last].transform_enter = strdup(transform_enter);
 216     }
 217     known_schemas[last].transform_onleave = transform_onleave;
 218     if (after_transform == 0) {
 219         after_transform = xml_schema_max;  
 220     }
 221     known_schemas[last].after_transform = after_transform;
 222 
 223     if (known_schemas[last].after_transform < 0) {
 224         crm_debug("Added supported schema %d: %s",
 225                   last, known_schemas[last].name);
 226 
 227     } else if (known_schemas[last].transform) {
 228         crm_debug("Added supported schema %d: %s (upgrades to %d with %s.xsl)",
 229                   last, known_schemas[last].name,
 230                   known_schemas[last].after_transform,
 231                   known_schemas[last].transform);
 232 
 233     } else {
 234         crm_debug("Added supported schema %d: %s (upgrades to %d)",
 235                   last, known_schemas[last].name,
 236                   known_schemas[last].after_transform);
 237     }
 238 }
 239 
 240 
 241 
 242 
 243 
 244 
 245 
 246 
 247 
 248 
 249 
 250 
 251 
 252 
 253 
 254 
 255 
 256 
 257 
 258 
 259 
 260 
 261 
 262 
 263 
 264 
 265 
 266 
 267 
 268 static int
 269 add_schema_by_version(const schema_version_t *version, int next,
     
 270                       bool transform_expected)
 271 {
 272     bool transform_onleave = FALSE;
 273     int rc = pcmk_rc_ok;
 274     struct stat s;
 275     char *xslt = NULL,
 276          *transform_upgrade = NULL,
 277          *transform_enter = NULL;
 278 
 279     
 280     if (transform_expected) {
 281         
 282         transform_upgrade = schema_strdup_printf("upgrade-", *version, );
 283         xslt = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt,
 284                                        transform_upgrade);
 285     }
 286 
 287     if (!transform_expected) {
 288         
 289 
 290     } else if (stat(xslt, &s) == 0) {
 291         
 292         transform_enter = schema_strdup_printf("upgrade-", *version, "-enter");
 293         free(xslt);
 294         xslt = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt,
 295                                        transform_enter);
 296         if (stat(xslt, &s) != 0) {
 297             
 298             crm_debug("Upgrade-enter transform %s.xsl not found", xslt);
 299             free(xslt);
 300             free(transform_enter);
 301             transform_enter = strdup("upgrade-enter");
 302             xslt = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt,
 303                                            transform_enter);
 304             if (stat(xslt, &s) != 0) {
 305                 crm_debug("Upgrade-enter transform %s.xsl not found, either", xslt);
 306                 free(xslt);
 307                 xslt = NULL;
 308             }
 309         }
 310         
 311         if (xslt != NULL) {
 312             
 313             memcpy(strrchr(xslt, '-') + 1, "leave", sizeof("leave") - 1);
 314             transform_onleave = (stat(xslt, &s) == 0);
 315             free(xslt);
 316         } else {
 317             free(transform_enter);
 318             transform_enter = NULL;
 319         }
 320 
 321     } else {
 322         crm_err("Upgrade transform %s not found", xslt);
 323         free(xslt);
 324         free(transform_upgrade);
 325         transform_upgrade = NULL;
 326         next = -1;
 327         rc = ENOENT;
 328     }
 329 
 330     add_schema(schema_validator_rng, version, NULL,
 331                transform_upgrade, transform_enter, transform_onleave, next);
 332 
 333     free(transform_upgrade);
 334     free(transform_enter);
 335 
 336     return rc;
 337 }
 338 
 339 static void
 340 wrap_libxslt(bool finalize)
     
 341 {
 342     static xsltSecurityPrefsPtr secprefs;
 343     int ret = 0;
 344 
 345     
 346     if (!finalize) {
 347         CRM_ASSERT(secprefs == NULL);
 348         secprefs = xsltNewSecurityPrefs();
 349         ret = xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_WRITE_FILE,
 350                                    xsltSecurityForbid)
 351               | xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_CREATE_DIRECTORY,
 352                                      xsltSecurityForbid)
 353               | xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_READ_NETWORK,
 354                                      xsltSecurityForbid)
 355               | xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_WRITE_NETWORK,
 356                                      xsltSecurityForbid);
 357         if (ret != 0) {
 358             return;
 359         }
 360     } else {
 361         xsltFreeSecurityPrefs(secprefs);
 362         secprefs = NULL;
 363     }
 364 
 365     
 366     if (finalize) {
 367         xsltCleanupGlobals();
 368     }
 369 }
 370 
 371 
 372 
 373 
 374 
 375 
 376 
 377 
 378 void
 379 crm_schema_init(void)
     
 380 {
 381     int lpc, max;
 382     char *base = pcmk__xml_artefact_root(pcmk__xml_artefact_ns_legacy_rng);
 383     struct dirent **namelist = NULL;
 384     const schema_version_t zero = SCHEMA_ZERO;
 385 
 386     wrap_libxslt(false);
 387 
 388     max = scandir(base, &namelist, schema_filter, schema_sort);
 389     if (max < 0) {
 390         crm_notice("scandir(%s) failed: %s (%d)", base, strerror(errno), errno);
 391         free(base);
 392 
 393     } else {
 394         free(base);
 395         for (lpc = 0; lpc < max; lpc++) {
 396             bool transform_expected = FALSE;
 397             int next = 0;
 398             schema_version_t version = SCHEMA_ZERO;
 399 
 400             if (!version_from_filename(namelist[lpc]->d_name, &version)) {
 401                 
 402                 crm_err("Skipping schema '%s': could not parse version",
 403                         namelist[lpc]->d_name);
 404                 continue;
 405             }
 406             if ((lpc + 1) < max) {
 407                 schema_version_t next_version = SCHEMA_ZERO;
 408 
 409                 if (version_from_filename(namelist[lpc+1]->d_name, &next_version)
 410                         && (version.v[0] < next_version.v[0])) {
 411                     transform_expected = TRUE;
 412                 }
 413 
 414             } else {
 415                 next = -1;
 416             }
 417             if (add_schema_by_version(&version, next, transform_expected)
 418                     == ENOENT) {
 419                 break;
 420             }
 421         }
 422 
 423         for (lpc = 0; lpc < max; lpc++) {
 424             free(namelist[lpc]);
 425         }
 426         free(namelist);
 427     }
 428 
 429     add_schema(schema_validator_rng, &zero, "pacemaker-next",
 430                NULL, NULL, FALSE, -1);
 431 
 432     add_schema(schema_validator_none, &zero, "none", NULL, NULL, FALSE, -1);
 433 }
 434 
 435 #if 0
 436 static void
 437 relaxng_invalid_stderr(void *userData, xmlErrorPtr error)
     
 438 {
 439     
 440 
 441 
 442 
 443 
 444 
 445 
 446 
 447 
 448 
 449 
 450 
 451 
 452 
 453 
 454 
 455 
 456 
 457     crm_err("Structured error: line=%d, level=%d %s", error->line, error->level, error->message);
 458 }
 459 #endif
 460 
 461 static gboolean
 462 validate_with_relaxng(xmlDocPtr doc, gboolean to_logs, const char *relaxng_file,
     
 463                       relaxng_ctx_cache_t **cached_ctx)
 464 {
 465     int rc = 0;
 466     gboolean valid = TRUE;
 467     relaxng_ctx_cache_t *ctx = NULL;
 468 
 469     CRM_CHECK(doc != NULL, return FALSE);
 470     CRM_CHECK(relaxng_file != NULL, return FALSE);
 471 
 472     if (cached_ctx && *cached_ctx) {
 473         ctx = *cached_ctx;
 474 
 475     } else {
 476         crm_debug("Creating RNG parser context");
 477         ctx = calloc(1, sizeof(relaxng_ctx_cache_t));
 478 
 479         xmlLoadExtDtdDefaultValue = 1;
 480         ctx->parser = xmlRelaxNGNewParserCtxt(relaxng_file);
 481         CRM_CHECK(ctx->parser != NULL, goto cleanup);
 482 
 483         if (to_logs) {
 484             xmlRelaxNGSetParserErrors(ctx->parser,
 485                                       (xmlRelaxNGValidityErrorFunc) xml_log,
 486                                       (xmlRelaxNGValidityWarningFunc) xml_log,
 487                                       GUINT_TO_POINTER(LOG_ERR));
 488         } else {
 489             xmlRelaxNGSetParserErrors(ctx->parser,
 490                                       (xmlRelaxNGValidityErrorFunc) fprintf,
 491                                       (xmlRelaxNGValidityWarningFunc) fprintf,
 492                                       stderr);
 493         }
 494 
 495         ctx->rng = xmlRelaxNGParse(ctx->parser);
 496         CRM_CHECK(ctx->rng != NULL,
 497                   crm_err("Could not find/parse %s", relaxng_file);
 498                   goto cleanup);
 499 
 500         ctx->valid = xmlRelaxNGNewValidCtxt(ctx->rng);
 501         CRM_CHECK(ctx->valid != NULL, goto cleanup);
 502 
 503         if (to_logs) {
 504             xmlRelaxNGSetValidErrors(ctx->valid,
 505                                      (xmlRelaxNGValidityErrorFunc) xml_log,
 506                                      (xmlRelaxNGValidityWarningFunc) xml_log,
 507                                      GUINT_TO_POINTER(LOG_ERR));
 508         } else {
 509             xmlRelaxNGSetValidErrors(ctx->valid,
 510                                      (xmlRelaxNGValidityErrorFunc) fprintf,
 511                                      (xmlRelaxNGValidityWarningFunc) fprintf,
 512                                      stderr);
 513         }
 514     }
 515 
 516     
 517     
 518 
 519     xmlLineNumbersDefault(1);
 520     rc = xmlRelaxNGValidateDoc(ctx->valid, doc);
 521     if (rc > 0) {
 522         valid = FALSE;
 523 
 524     } else if (rc < 0) {
 525         crm_err("Internal libxml error during validation");
 526     }
 527 
 528   cleanup:
 529 
 530     if (cached_ctx) {
 531         *cached_ctx = ctx;
 532 
 533     } else {
 534         if (ctx->parser != NULL) {
 535             xmlRelaxNGFreeParserCtxt(ctx->parser);
 536         }
 537         if (ctx->valid != NULL) {
 538             xmlRelaxNGFreeValidCtxt(ctx->valid);
 539         }
 540         if (ctx->rng != NULL) {
 541             xmlRelaxNGFree(ctx->rng);
 542         }
 543         free(ctx);
 544     }
 545 
 546     return valid;
 547 }
 548 
 549 
 550 
 551 
 552 
 553 void
 554 crm_schema_cleanup(void)
     
 555 {
 556     int lpc;
 557     relaxng_ctx_cache_t *ctx = NULL;
 558 
 559     for (lpc = 0; lpc < xml_schema_max; lpc++) {
 560 
 561         switch (known_schemas[lpc].validator) {
 562             case schema_validator_none: 
 563                 break;
 564             case schema_validator_rng: 
 565                 ctx = (relaxng_ctx_cache_t *) known_schemas[lpc].cache;
 566                 if (ctx == NULL) {
 567                     break;
 568                 }
 569                 if (ctx->parser != NULL) {
 570                     xmlRelaxNGFreeParserCtxt(ctx->parser);
 571                 }
 572                 if (ctx->valid != NULL) {
 573                     xmlRelaxNGFreeValidCtxt(ctx->valid);
 574                 }
 575                 if (ctx->rng != NULL) {
 576                     xmlRelaxNGFree(ctx->rng);
 577                 }
 578                 free(ctx);
 579                 known_schemas[lpc].cache = NULL;
 580                 break;
 581         }
 582         free(known_schemas[lpc].name);
 583         free(known_schemas[lpc].transform);
 584         free(known_schemas[lpc].transform_enter);
 585     }
 586     free(known_schemas);
 587     known_schemas = NULL;
 588 
 589     wrap_libxslt(true);
 590 }
 591 
 592 static gboolean
 593 validate_with(xmlNode *xml, int method, gboolean to_logs)
     
 594 {
 595     xmlDocPtr doc = NULL;
 596     gboolean valid = FALSE;
 597     char *file = NULL;
 598 
 599     if (method < 0) {
 600         return FALSE;
 601     }
 602 
 603     if (known_schemas[method].validator == schema_validator_none) {
 604         return TRUE;
 605     }
 606 
 607     CRM_CHECK(xml != NULL, return FALSE);
 608     doc = getDocPtr(xml);
 609     file = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_rng,
 610                                    known_schemas[method].name);
 611 
 612     crm_trace("Validating with: %s (type=%d)",
 613               crm_str(file), known_schemas[method].validator);
 614     switch (known_schemas[method].validator) {
 615         case schema_validator_rng:
 616             valid =
 617                 validate_with_relaxng(doc, to_logs, file,
 618                                       (relaxng_ctx_cache_t **) & (known_schemas[method].cache));
 619             break;
 620         default:
 621             crm_err("Unknown validator type: %d",
 622                     known_schemas[method].validator);
 623             break;
 624     }
 625 
 626     free(file);
 627     return valid;
 628 }
 629 
 630 static bool
 631 validate_with_silent(xmlNode *xml, int method)
     
 632 {
 633     bool rc, sl_backup = silent_logging;
 634     silent_logging = TRUE;
 635     rc = validate_with(xml, method, TRUE);
 636     silent_logging = sl_backup;
 637     return rc;
 638 }
 639 
 640 static void
 641 dump_file(const char *filename)
     
 642 {
 643 
 644     FILE *fp = NULL;
 645     int ch, line = 0;
 646 
 647     CRM_CHECK(filename != NULL, return);
 648 
 649     fp = fopen(filename, "r");
 650     if (fp == NULL) {
 651         crm_perror(LOG_ERR, "Could not open %s for reading", filename);
 652         return;
 653     }
 654 
 655     fprintf(stderr, "%4d ", ++line);
 656     do {
 657         ch = getc(fp);
 658         if (ch == EOF) {
 659             putc('\n', stderr);
 660             break;
 661         } else if (ch == '\n') {
 662             fprintf(stderr, "\n%4d ", ++line);
 663         } else {
 664             putc(ch, stderr);
 665         }
 666     } while (1);
 667 
 668     fclose(fp);
 669 }
 670 
 671 gboolean
 672 validate_xml_verbose(xmlNode *xml_blob)
     
 673 {
 674     int fd = 0;
 675     xmlDoc *doc = NULL;
 676     xmlNode *xml = NULL;
 677     gboolean rc = FALSE;
 678     char *filename = NULL;
 679 
 680     filename = crm_strdup_printf("%s/cib-invalid.XXXXXX", pcmk__get_tmpdir());
 681 
 682     umask(S_IWGRP | S_IWOTH | S_IROTH);
 683     fd = mkstemp(filename);
 684     write_xml_fd(xml_blob, filename, fd, FALSE);
 685 
 686     dump_file(filename);
 687 
 688     doc = xmlParseFile(filename);
 689     xml = xmlDocGetRootElement(doc);
 690     rc = validate_xml(xml, NULL, FALSE);
 691     free_xml(xml);
 692 
 693     unlink(filename);
 694     free(filename);
 695 
 696     return rc;
 697 }
 698 
 699 gboolean
 700 validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
     
 701 {
 702     int version = 0;
 703 
 704     if (validation == NULL) {
 705         validation = crm_element_value(xml_blob, XML_ATTR_VALIDATION);
 706     }
 707 
 708     if (validation == NULL) {
 709         int lpc = 0;
 710         bool valid = FALSE;
 711 
 712         for (lpc = 0; lpc < xml_schema_max; lpc++) {
 713             if (validate_with(xml_blob, lpc, FALSE)) {
 714                 valid = TRUE;
 715                 crm_xml_add(xml_blob, XML_ATTR_VALIDATION,
 716                             known_schemas[lpc].name);
 717                 crm_info("XML validated against %s", known_schemas[lpc].name);
 718                 if(known_schemas[lpc].after_transform == 0) {
 719                     break;
 720                 }
 721             }
 722         }
 723 
 724         return valid;
 725     }
 726 
 727     version = get_schema_version(validation);
 728     if (strcmp(validation, "none") == 0) {
 729         return TRUE;
 730     } else if (version < xml_schema_max) {
 731         return validate_with(xml_blob, version, to_logs);
 732     }
 733 
 734     crm_err("Unknown validator: %s", validation);
 735     return FALSE;
 736 }
 737 
 738 #if HAVE_LIBXSLT
 739 
 740 static void
 741 cib_upgrade_err(void *ctx, const char *fmt, ...)
     
 742 G_GNUC_PRINTF(2, 3);
 743 
 744 
 745 
 746 
 747 
 748 
 749 
 750 
 751 
 752 
 753 
 754 
 755 
 756 
 757 
 758 
 759 
 760 
 761 
 762 
 763 
 764 
 765 
 766 
 767 static void
 768 cib_upgrade_err(void *ctx, const char *fmt, ...)
 769 {
 770     va_list ap, aq;
 771     char *arg_cur;
 772 
 773     bool found = FALSE;
 774     const char *fmt_iter = fmt;
 775     uint8_t msg_log_level = LOG_WARNING;  
 776     const unsigned * log_level = (const unsigned *) ctx;
 777     enum {
 778         escan_seennothing,
 779         escan_seenpercent,
 780     } scan_state = escan_seennothing;
 781 
 782     va_start(ap, fmt);
 783     va_copy(aq, ap);
 784 
 785     while (!found && *fmt_iter != '\0') {
 786         
 787         switch (*fmt_iter++) {
 788         case '%':
 789             if (scan_state == escan_seennothing) {
 790                 scan_state = escan_seenpercent;
 791             } else if (scan_state == escan_seenpercent) {
 792                 scan_state = escan_seennothing;
 793             }
 794             break;
 795         case 's':
 796             if (scan_state == escan_seenpercent) {
 797                 scan_state = escan_seennothing;
 798                 arg_cur = va_arg(aq, char *);
 799                 if (arg_cur != NULL) {
 800                     switch (arg_cur[0]) {
 801                     case 'W':
 802                         if (!strncmp(arg_cur, "WARNING: ",
 803                                      sizeof("WARNING: ") - 1)) {
 804                             msg_log_level = LOG_WARNING;
 805                         }
 806                         if (ctx == NULL) {
 807                             memmove(arg_cur, arg_cur + sizeof("WARNING: ") - 1,
 808                                     strlen(arg_cur + sizeof("WARNING: ") - 1) + 1);
 809                         }
 810                         found = TRUE;
 811                         break;
 812                     case 'I':
 813                         if (!strncmp(arg_cur, "INFO: ",
 814                                      sizeof("INFO: ") - 1)) {
 815                             msg_log_level = LOG_INFO;
 816                         }
 817                         if (ctx == NULL) {
 818                             memmove(arg_cur, arg_cur + sizeof("INFO: ") - 1,
 819                                     strlen(arg_cur + sizeof("INFO: ") - 1) + 1);
 820                         }
 821                         found = TRUE;
 822                         break;
 823                     case 'D':
 824                         if (!strncmp(arg_cur, "DEBUG: ",
 825                                      sizeof("DEBUG: ") - 1)) {
 826                             msg_log_level = LOG_DEBUG;
 827                         }
 828                         if (ctx == NULL) {
 829                             memmove(arg_cur, arg_cur + sizeof("DEBUG: ") - 1,
 830                                     strlen(arg_cur + sizeof("DEBUG: ") - 1) + 1);
 831                         }
 832                         found = TRUE;
 833                         break;
 834                     }
 835                 }
 836             }
 837             break;
 838         case '#': case '-': case ' ': case '+': case '\'': case 'I': case '.':
 839         case '0': case '1': case '2': case '3': case '4':
 840         case '5': case '6': case '7': case '8': case '9':
 841         case '*':
 842             break;
 843         case 'l':
 844         case 'z':
 845         case 't':
 846         case 'j':
 847         case 'd': case 'i':
 848         case 'o':
 849         case 'u':
 850         case 'x': case 'X':
 851         case 'e': case 'E':
 852         case 'f': case 'F':
 853         case 'g': case 'G':
 854         case 'a': case 'A':
 855         case 'c':
 856         case 'p':
 857             if (scan_state == escan_seenpercent) {
 858                 (void) va_arg(aq, void *);  
 859                 scan_state = escan_seennothing;
 860             }
 861             break;
 862         default:
 863             scan_state = escan_seennothing;
 864             break;
 865         }
 866     }
 867 
 868     if (log_level != NULL) {
 869         
 870 
 871         if (*log_level + 4 >= msg_log_level) {
 872             vfprintf(stderr, fmt, ap);
 873         }
 874     } else {
 875         PCMK__XML_LOG_BASE(msg_log_level, TRUE, 0, "CIB upgrade: ", fmt, ap);
 876     }
 877 
 878     va_end(aq);
 879     va_end(ap);
 880 }
 881 
 882 
 883 
 884 
 885 
 886 
 887 
 888 
 889 
 890 
 891 
 892 
 893 
 894 
 895 
 896 
 897 #ifndef PCMK_SCHEMAS_EMERGENCY_XSLT
 898 #define PCMK_SCHEMAS_EMERGENCY_XSLT 1
 899 #endif
 900 
 901 static xmlNode *
 902 apply_transformation(xmlNode *xml, const char *transform, gboolean to_logs)
     
 903 {
 904     char *xform = NULL;
 905     xmlNode *out = NULL;
 906     xmlDocPtr res = NULL;
 907     xmlDocPtr doc = NULL;
 908     xsltStylesheet *xslt = NULL;
 909 #if PCMK_SCHEMAS_EMERGENCY_XSLT != 0
 910     xmlChar *emergency_result;
 911     int emergency_txt_len;
 912     int emergency_res;
 913 #endif
 914 
 915     CRM_CHECK(xml != NULL, return FALSE);
 916     doc = getDocPtr(xml);
 917     xform = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt,
 918                                     transform);
 919 
 920     xmlLoadExtDtdDefaultValue = 1;
 921     xmlSubstituteEntitiesDefault(1);
 922 
 923     
 924     if (to_logs) {
 925         xsltSetGenericErrorFunc(NULL, cib_upgrade_err);
 926     } else {
 927         xsltSetGenericErrorFunc(&crm_log_level, cib_upgrade_err);
 928     }
 929 
 930     xslt = xsltParseStylesheetFile((pcmkXmlStr) xform);
 931     CRM_CHECK(xslt != NULL, goto cleanup);
 932 
 933     res = xsltApplyStylesheet(xslt, doc, NULL);
 934     CRM_CHECK(res != NULL, goto cleanup);
 935 
 936     xsltSetGenericErrorFunc(NULL, NULL);  
 937 
 938 
 939 #if PCMK_SCHEMAS_EMERGENCY_XSLT != 0
 940     emergency_res = xsltSaveResultToString(&emergency_result,
 941                                            &emergency_txt_len, res, xslt);
 942     xmlFreeDoc(res);
 943     CRM_CHECK(emergency_res == 0, goto cleanup);
 944     out = string2xml((const char *) emergency_result);
 945     free(emergency_result);
 946 #else
 947     out = xmlDocGetRootElement(res);
 948 #endif
 949 
 950   cleanup:
 951     if (xslt) {
 952         xsltFreeStylesheet(xslt);
 953     }
 954 
 955     free(xform);
 956 
 957     return out;
 958 }
 959 
 960 
 961 
 962 
 963 
 964 
 965 
 966 static xmlNode *
 967 apply_upgrade(xmlNode *xml, const struct schema_s *schema, gboolean to_logs)
     
 968 {
 969     bool transform_onleave = schema->transform_onleave;
 970     char *transform_leave;
 971     xmlNode *upgrade = NULL,
 972             *final = NULL;
 973 
 974     if (schema->transform_enter) {
 975         crm_debug("Upgrading %s-style configuration, pre-upgrade phase with %s.xsl",
 976                   schema->name, schema->transform_enter);
 977         upgrade = apply_transformation(xml, schema->transform_enter, to_logs);
 978         if (upgrade == NULL) {
 979             crm_warn("Upgrade-enter transformation %s.xsl failed",
 980                      schema->transform_enter);
 981             transform_onleave = FALSE;
 982         }
 983     }
 984     if (upgrade == NULL) {
 985         upgrade = xml;
 986     }
 987 
 988     crm_debug("Upgrading %s-style configuration, main phase with %s.xsl",
 989               schema->name, schema->transform);
 990     final = apply_transformation(upgrade, schema->transform, to_logs);
 991     if (upgrade != xml) {
 992         free_xml(upgrade);
 993         upgrade = NULL;
 994     }
 995 
 996     if (final != NULL && transform_onleave) {
 997         upgrade = final;
 998         
 999         CRM_ASSERT(schema->transform_enter != NULL);
1000         transform_leave = strdup(schema->transform_enter);
1001         
1002         memcpy(strrchr(transform_leave, '-') + 1, "leave", sizeof("leave") - 1);
1003         crm_debug("Upgrading %s-style configuration, post-upgrade phase with %s.xsl",
1004                   schema->name, transform_leave);
1005         final = apply_transformation(upgrade, transform_leave, to_logs);
1006         if (final == NULL) {
1007             crm_warn("Upgrade-leave transformation %s.xsl failed", transform_leave);
1008             final = upgrade;
1009         } else {
1010             free_xml(upgrade);
1011         }
1012         free(transform_leave);
1013     }
1014 
1015     return final;
1016 }
1017 
1018 #endif  
1019 
1020 const char *
1021 get_schema_name(int version)
     
1022 {
1023     if (version < 0 || version >= xml_schema_max) {
1024         return "unknown";
1025     }
1026     return known_schemas[version].name;
1027 }
1028 
1029 int
1030 get_schema_version(const char *name)
     
1031 {
1032     int lpc = 0;
1033 
1034     if (name == NULL) {
1035         name = "none";
1036     }
1037     for (; lpc < xml_schema_max; lpc++) {
1038         if (pcmk__str_eq(name, known_schemas[lpc].name, pcmk__str_casei)) {
1039             return lpc;
1040         }
1041     }
1042     return -1;
1043 }
1044 
1045 
1046 int
1047 update_validation(xmlNode **xml_blob, int *best, int max, gboolean transform,
     
1048                   gboolean to_logs)
1049 {
1050     xmlNode *xml = NULL;
1051     char *value = NULL;
1052     int max_stable_schemas = xml_latest_schema_index();
1053     int lpc = 0, match = -1, rc = pcmk_ok;
1054     int next = -1;  
1055 
1056     CRM_CHECK(best != NULL, return -EINVAL);
1057     *best = 0;
1058 
1059     CRM_CHECK(xml_blob != NULL, return -EINVAL);
1060     CRM_CHECK(*xml_blob != NULL, return -EINVAL);
1061 
1062     xml = *xml_blob;
1063     value = crm_element_value_copy(xml, XML_ATTR_VALIDATION);
1064 
1065     if (value != NULL) {
1066         match = get_schema_version(value);
1067 
1068         lpc = match;
1069         if (lpc >= 0 && transform == FALSE) {
1070             *best = lpc++;
1071 
1072         } else if (lpc < 0) {
1073             crm_debug("Unknown validation schema");
1074             lpc = 0;
1075         }
1076     }
1077 
1078     if (match >= max_stable_schemas) {
1079         
1080         free(value);
1081         *best = match;
1082         return pcmk_ok;
1083     }
1084 
1085     while (lpc <= max_stable_schemas) {
1086         crm_debug("Testing '%s' validation (%d of %d)",
1087                   known_schemas[lpc].name ? known_schemas[lpc].name : "<unset>",
1088                   lpc, max_stable_schemas);
1089 
1090         if (validate_with(xml, lpc, to_logs) == FALSE) {
1091             if (next != -1) {
1092                 crm_info("Configuration not valid for schema: %s",
1093                          known_schemas[lpc].name);
1094                 next = -1;
1095             } else {
1096                 crm_trace("%s validation failed",
1097                           known_schemas[lpc].name ? known_schemas[lpc].name : "<unset>");
1098             }
1099             if (*best) {
1100                 
1101                 break;
1102             }
1103             rc = -pcmk_err_schema_validation;
1104 
1105         } else {
1106             if (next != -1) {
1107                 crm_debug("Configuration valid for schema: %s",
1108                           known_schemas[next].name);
1109                 next = -1;
1110             }
1111             rc = pcmk_ok;
1112         }
1113 
1114         if (rc == pcmk_ok) {
1115             *best = lpc;
1116         }
1117 
1118         if (rc == pcmk_ok && transform) {
1119             xmlNode *upgrade = NULL;
1120             next = known_schemas[lpc].after_transform;
1121 
1122             if (next <= lpc) {
1123                 
1124                 crm_trace("Stopping at %s", known_schemas[lpc].name);
1125                 break;
1126 
1127             } else if (max > 0 && (lpc == max || next > max)) {
1128                 crm_trace("Upgrade limit reached at %s (lpc=%d, next=%d, max=%d)",
1129                           known_schemas[lpc].name, lpc, next, max);
1130                 break;
1131 
1132             } else if (known_schemas[lpc].transform == NULL
1133                        
1134 
1135 
1136 
1137 
1138                        || validate_with_silent(xml, next)) {
1139                 crm_debug("%s-style configuration is also valid for %s",
1140                            known_schemas[lpc].name, known_schemas[next].name);
1141 
1142                 lpc = next;
1143 
1144             } else {
1145                 crm_debug("Upgrading %s-style configuration to %s with %s.xsl",
1146                            known_schemas[lpc].name, known_schemas[next].name,
1147                            known_schemas[lpc].transform);
1148 
1149 #if HAVE_LIBXSLT
1150                 upgrade = apply_upgrade(xml, &known_schemas[lpc], to_logs);
1151 #endif
1152                 if (upgrade == NULL) {
1153                     crm_err("Transformation %s.xsl failed",
1154                             known_schemas[lpc].transform);
1155                     rc = -pcmk_err_transform_failed;
1156 
1157                 } else if (validate_with(upgrade, next, to_logs)) {
1158                     crm_info("Transformation %s.xsl successful",
1159                              known_schemas[lpc].transform);
1160                     lpc = next;
1161                     *best = next;
1162                     free_xml(xml);
1163                     xml = upgrade;
1164                     rc = pcmk_ok;
1165 
1166                 } else {
1167                     crm_err("Transformation %s.xsl did not produce a valid configuration",
1168                             known_schemas[lpc].transform);
1169                     crm_log_xml_info(upgrade, "transform:bad");
1170                     free_xml(upgrade);
1171                     rc = -pcmk_err_schema_validation;
1172                 }
1173                 next = -1;
1174             }
1175         }
1176 
1177         if (transform == FALSE || rc != pcmk_ok) {
1178             
1179             lpc++;
1180         }
1181     }
1182 
1183     if (*best > match && *best) {
1184         crm_info("%s the configuration from %s to %s",
1185                    transform?"Transformed":"Upgraded",
1186                    value ? value : "<none>", known_schemas[*best].name);
1187         crm_xml_add(xml, XML_ATTR_VALIDATION, known_schemas[*best].name);
1188     }
1189 
1190     *xml_blob = xml;
1191     free(value);
1192     return rc;
1193 }
1194 
1195 gboolean
1196 cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs)
     
1197 {
1198     gboolean rc = TRUE;
1199     const char *value = crm_element_value(*xml, XML_ATTR_VALIDATION);
1200     char *const orig_value = strdup(value == NULL ? "(none)" : value);
1201 
1202     int version = get_schema_version(value);
1203     int orig_version = version;
1204     int min_version = xml_minimum_schema_index();
1205 
1206     if (version < min_version) {
1207         
1208         xmlNode *converted = NULL;
1209 
1210         converted = copy_xml(*xml);
1211         update_validation(&converted, &version, 0, TRUE, to_logs);
1212 
1213         value = crm_element_value(converted, XML_ATTR_VALIDATION);
1214         if (version < min_version) {
1215             
1216 
1217             if (version < orig_version || orig_version == -1) {
1218                 
1219                 if (to_logs) {
1220                     pcmk__config_err("Cannot upgrade configuration (claiming "
1221                                      "schema %s) to at least %s because it "
1222                                      "does not validate with any schema from "
1223                                      "%s to %s",
1224                                      orig_value,
1225                                      get_schema_name(min_version),
1226                                      get_schema_name(orig_version),
1227                                      xml_latest_schema());
1228                 } else {
1229                     fprintf(stderr, "Cannot upgrade configuration (claiming "
1230                                     "schema %s) to at least %s because it "
1231                                     "does not validate with any schema from "
1232                                     "%s to %s\n",
1233                                     orig_value,
1234                                     get_schema_name(min_version),
1235                                     get_schema_name(orig_version),
1236                                     xml_latest_schema());
1237                 }
1238             } else {
1239                 
1240                 if (to_logs) {
1241                     pcmk__config_err("Cannot upgrade configuration (claiming "
1242                                      "schema %s) to at least %s because it "
1243                                      "would not upgrade past %s",
1244                                      orig_value,
1245                                      get_schema_name(min_version),
1246                                      crm_str(value));
1247                 } else {
1248                     fprintf(stderr, "Cannot upgrade configuration (claiming "
1249                                     "schema %s) to at least %s because it "
1250                                     "would not upgrade past %s\n",
1251                                     orig_value,
1252                                     get_schema_name(min_version),
1253                                     crm_str(value));
1254                 }
1255             }
1256 
1257             free_xml(converted);
1258             converted = NULL;
1259             rc = FALSE;
1260 
1261         } else {
1262             
1263             free_xml(*xml);
1264             *xml = converted;
1265 
1266             if (version < xml_latest_schema_index()) {
1267                 if (to_logs) {
1268                     pcmk__config_warn("Configuration with schema %s was "
1269                                       "internally upgraded to acceptable (but "
1270                                       "not most recent) %s",
1271                                       orig_value, get_schema_name(version));
1272                 }
1273             } else {
1274                 if (to_logs) {
1275                     crm_info("Configuration with schema %s was internally "
1276                              "upgraded to latest version %s",
1277                              orig_value, get_schema_name(version));
1278                 }
1279             }
1280         }
1281 
1282     } else if (version >= get_schema_version("none")) {
1283         
1284         if (to_logs) {
1285             pcmk__config_warn("Schema validation of configuration is disabled "
1286                               "(enabling is encouraged and prevents common "
1287                               "misconfigurations)");
1288 
1289         } else {
1290             fprintf(stderr, "Schema validation of configuration is disabled "
1291                             "(enabling is encouraged and prevents common "
1292                             "misconfigurations)\n");
1293         }
1294     }
1295 
1296     if (best_version) {
1297         *best_version = version;
1298     }
1299 
1300     free(orig_value);
1301     return rc;
1302 }