19 #include <libxml/relaxng.h>    22 #  include <libxslt/xslt.h>    23 #  include <libxslt/transform.h>    24 #  include <libxslt/security.h>    25 #  include <libxslt/xsltutils.h>    36 #define SCHEMA_ZERO { .v = { 0, 0 } }    38 #define schema_scanf(s, prefix, version, suffix) \    39     sscanf((s), prefix "%hhu.%hhu" suffix, &((version).v[0]), &((version).v[1]))    41 #define schema_strdup_printf(prefix, version, suffix) \    42     crm_strdup_printf(prefix "%u.%u" suffix, (version).v[0], (version).v[1])    46     xmlRelaxNGValidCtxtPtr valid;
    47     xmlRelaxNGParserCtxtPtr parser;
    48 } relaxng_ctx_cache_t;
    62     char *transform_enter;
    63     bool transform_onleave;
    66 static struct schema_s *known_schemas = NULL;
    67 static int xml_schema_max = 0;
    68 static bool silent_logging = FALSE;
    71 xml_log(
int priority, 
const char *fmt, ...)
    75 xml_log(
int priority, const 
char *fmt, ...)
    80     if (silent_logging == FALSE) {
    88 xml_latest_schema_index(
void)
    90     return xml_schema_max - 3; 
    94 xml_minimum_schema_index(
void)
   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]) {
   109         best = xml_latest_schema_index();
   121 version_from_filename(
const char *filename, schema_version_t *
version)
   129 schema_filter(
const struct dirent *a)
   134     if (strstr(a->d_name, 
"pacemaker-") != a->d_name) {
   140     } 
else if (!version_from_filename(a->d_name, &
version)) {
   152 schema_sort(
const struct dirent **a, 
const struct dirent **b)
   157     if (!version_from_filename(a[0]->d_name, &a_version)
   158         || !version_from_filename(b[0]->d_name, &b_version)) {
   163     for (
int i = 0; i < 2; ++i) {
   164         if (a_version.v[i] < b_version.v[i]) {
   166         } 
else if (a_version.v[i] > b_version.v[i]) {
   182            const char *
name, 
const char *transform,
   183            const char *transform_enter, 
bool transform_onleave,
   186     int last = xml_schema_max;
   187     bool have_version = FALSE;
   190     known_schemas = pcmk__realloc(known_schemas,
   191                                   xml_schema_max * 
sizeof(
struct schema_s));
   193     memset(known_schemas+last, 0, 
sizeof(
struct schema_s));
   194     known_schemas[last].validator = validator;
   195     known_schemas[last].after_transform = after_transform;
   197     for (
int i = 0; i < 2; ++i) {
   198         known_schemas[last].version.v[i] = 
version->v[i];
   208         known_schemas[last].name = strdup(
name);
   212         known_schemas[last].transform = strdup(transform);
   214     if (transform_enter) {
   215         known_schemas[last].transform_enter = strdup(transform_enter);
   217     known_schemas[last].transform_onleave = transform_onleave;
   218     if (after_transform == 0) {
   219         after_transform = xml_schema_max;  
   221     known_schemas[last].after_transform = after_transform;
   223     if (known_schemas[last].after_transform < 0) {
   224         crm_debug(
"Added supported schema %d: %s",
   225                   last, known_schemas[last].
name);
   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);
   234         crm_debug(
"Added supported schema %d: %s (upgrades to %d)",
   235                   last, known_schemas[last].
name,
   236                   known_schemas[last].after_transform);
   269 add_schema_by_version(
const schema_version_t *
version, 
int next,
   270                       bool transform_expected)
   272     bool transform_onleave = FALSE;
   276          *transform_upgrade = NULL,
   277          *transform_enter = NULL;
   280     if (transform_expected) {
   287     if (!transform_expected) {
   290     } 
else if (stat(xslt, &s) == 0) {
   296         if (stat(xslt, &s) != 0) {
   298             crm_debug(
"Upgrade-enter transform %s.xsl not found", xslt);
   300             free(transform_enter);
   301             transform_enter = strdup(
"upgrade-enter");
   304             if (stat(xslt, &s) != 0) {
   305                 crm_debug(
"Upgrade-enter transform %s.xsl not found, either", xslt);
   313             memcpy(strrchr(xslt, 
'-') + 1, 
"leave", 
sizeof(
"leave") - 1);
   314             transform_onleave = (stat(xslt, &s) == 0);
   317             free(transform_enter);
   318             transform_enter = NULL;
   322         crm_err(
"Upgrade transform %s not found", xslt);
   324         free(transform_upgrade);
   325         transform_upgrade = NULL;
   331                transform_upgrade, transform_enter, transform_onleave, next);
   333     free(transform_upgrade);
   334     free(transform_enter);
   340 wrap_libxslt(
bool finalize)
   342     static xsltSecurityPrefsPtr secprefs;
   348         secprefs = xsltNewSecurityPrefs();
   349         ret = xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_WRITE_FILE,
   351               | xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_CREATE_DIRECTORY,
   353               | xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_READ_NETWORK,
   355               | xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_WRITE_NETWORK,
   361         xsltFreeSecurityPrefs(secprefs);
   367         xsltCleanupGlobals();
   383     struct dirent **namelist = NULL;
   388     max = scandir(base, &namelist, schema_filter, schema_sort);
   395         for (lpc = 0; lpc < max; lpc++) {
   396             bool transform_expected = FALSE;
   400             if (!version_from_filename(namelist[lpc]->d_name, &
version)) {
   402                 crm_err(
"Skipping schema '%s': could not parse version",
   403                         namelist[lpc]->d_name);
   406             if ((lpc + 1) < max) {
   409                 if (version_from_filename(namelist[lpc+1]->d_name, &next_version)
   410                         && (
version.v[0] < next_version.v[0])) {
   411                     transform_expected = TRUE;
   417             if (add_schema_by_version(&
version, next, transform_expected)
   423         for (lpc = 0; lpc < max; lpc++) {
   430                NULL, NULL, FALSE, -1);
   437 relaxng_invalid_stderr(
void *userData, xmlErrorPtr error)
   457     crm_err(
"Structured error: line=%d, level=%d %s", error->line, error->level, error->message);
   462 validate_with_relaxng(xmlDocPtr doc, gboolean to_logs, 
const char *relaxng_file,
   463                       relaxng_ctx_cache_t **cached_ctx)
   466     gboolean valid = TRUE;
   467     relaxng_ctx_cache_t *ctx = NULL;
   470     CRM_CHECK(relaxng_file != NULL, 
return FALSE);
   472     if (cached_ctx && *cached_ctx) {
   476         crm_debug(
"Creating RNG parser context");
   477         ctx = calloc(1, 
sizeof(relaxng_ctx_cache_t));
   479         xmlLoadExtDtdDefaultValue = 1;
   480         ctx->parser = xmlRelaxNGNewParserCtxt(relaxng_file);
   481         CRM_CHECK(ctx->parser != NULL, 
goto cleanup);
   484             xmlRelaxNGSetParserErrors(ctx->parser,
   485                                       (xmlRelaxNGValidityErrorFunc) xml_log,
   486                                       (xmlRelaxNGValidityWarningFunc) xml_log,
   487                                       GUINT_TO_POINTER(LOG_ERR));
   489             xmlRelaxNGSetParserErrors(ctx->parser,
   490                                       (xmlRelaxNGValidityErrorFunc) fprintf,
   491                                       (xmlRelaxNGValidityWarningFunc) fprintf,
   495         ctx->rng = xmlRelaxNGParse(ctx->parser);
   497                   crm_err(
"Could not find/parse %s", relaxng_file);
   500         ctx->valid = xmlRelaxNGNewValidCtxt(ctx->rng);
   501         CRM_CHECK(ctx->valid != NULL, 
goto cleanup);
   504             xmlRelaxNGSetValidErrors(ctx->valid,
   505                                      (xmlRelaxNGValidityErrorFunc) xml_log,
   506                                      (xmlRelaxNGValidityWarningFunc) xml_log,
   507                                      GUINT_TO_POINTER(LOG_ERR));
   509             xmlRelaxNGSetValidErrors(ctx->valid,
   510                                      (xmlRelaxNGValidityErrorFunc) fprintf,
   511                                      (xmlRelaxNGValidityWarningFunc) fprintf,
   519     xmlLineNumbersDefault(1);
   520     rc = xmlRelaxNGValidateDoc(ctx->valid, doc);
   525         crm_err(
"Internal libxml error during validation");
   534         if (ctx->parser != NULL) {
   535             xmlRelaxNGFreeParserCtxt(ctx->parser);
   537         if (ctx->valid != NULL) {
   538             xmlRelaxNGFreeValidCtxt(ctx->valid);
   540         if (ctx->rng != NULL) {
   541             xmlRelaxNGFree(ctx->rng);
   557     relaxng_ctx_cache_t *ctx = NULL;
   559     for (lpc = 0; lpc < xml_schema_max; lpc++) {
   561         switch (known_schemas[lpc].validator) {
   565                 ctx = (relaxng_ctx_cache_t *) known_schemas[lpc].cache;
   569                 if (ctx->parser != NULL) {
   570                     xmlRelaxNGFreeParserCtxt(ctx->parser);
   572                 if (ctx->valid != NULL) {
   573                     xmlRelaxNGFreeValidCtxt(ctx->valid);
   575                 if (ctx->rng != NULL) {
   576                     xmlRelaxNGFree(ctx->rng);
   579                 known_schemas[lpc].cache = NULL;
   582         free(known_schemas[lpc].
name);
   583         free(known_schemas[lpc].transform);
   584         free(known_schemas[lpc].transform_enter);
   587     known_schemas = NULL;
   593 validate_with(xmlNode *xml, 
int method, gboolean to_logs)
   595     xmlDocPtr doc = NULL;
   596     gboolean valid = FALSE;
   610                                    known_schemas[method].
name);
   612     crm_trace(
"Validating with: %s (type=%d)",
   613               crm_str(file), known_schemas[method].validator);
   614     switch (known_schemas[method].validator) {
   617                 validate_with_relaxng(doc, to_logs, file,
   618                                       (relaxng_ctx_cache_t **) & (known_schemas[method].cache));
   621             crm_err(
"Unknown validator type: %d",
   622                     known_schemas[method].validator);
   631 validate_with_silent(xmlNode *xml, 
int method)
   633     bool rc, sl_backup = silent_logging;
   634     silent_logging = TRUE;
   635     rc = validate_with(xml, method, TRUE);
   636     silent_logging = sl_backup;
   641 dump_file(
const char *filename)
   649     fp = fopen(filename, 
"r");
   651         crm_perror(LOG_ERR, 
"Could not open %s for reading", filename);
   655     fprintf(stderr, 
"%4d ", ++line);
   661         } 
else if (ch == 
'\n') {
   662             fprintf(stderr, 
"\n%4d ", ++line);
   678     char *filename = NULL;
   682     umask(S_IWGRP | S_IWOTH | S_IROTH);
   683     fd = mkstemp(filename);
   688     doc = xmlParseFile(filename);
   689     xml = xmlDocGetRootElement(doc);
   700 validate_xml(xmlNode *xml_blob, 
const char *validation, gboolean to_logs)
   704     if (validation == NULL) {
   708     if (validation == NULL) {
   712         for (lpc = 0; lpc < xml_schema_max; lpc++) {
   713             if (validate_with(xml_blob, lpc, FALSE)) {
   716                             known_schemas[lpc].
name);
   717                 crm_info(
"XML validated against %s", known_schemas[lpc].
name);
   718                 if(known_schemas[lpc].after_transform == 0) {
   728     if (strcmp(validation, 
"none") == 0) {
   730     } 
else if (
version < xml_schema_max) {
   731         return validate_with(xml_blob, 
version, to_logs);
   734     crm_err(
"Unknown validator: %s", validation);
   741 cib_upgrade_err(
void *ctx, 
const char *fmt, ...)
   768 cib_upgrade_err(
void *ctx, const 
char *fmt, ...)
   774     const char *fmt_iter = fmt;
   775     uint8_t msg_log_level = LOG_WARNING;  
   776     const unsigned * log_level = (
const unsigned *) ctx;
   780     } scan_state = escan_seennothing;
   785     while (!found && *fmt_iter != 
'\0') {
   787         switch (*fmt_iter++) {
   789             if (scan_state == escan_seennothing) {
   790                 scan_state = escan_seenpercent;
   791             } 
else if (scan_state == escan_seenpercent) {
   792                 scan_state = escan_seennothing;
   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]) {
   802                         if (!strncmp(arg_cur, 
"WARNING: ",
   803                                      sizeof(
"WARNING: ") - 1)) {
   804                             msg_log_level = LOG_WARNING;
   807                             memmove(arg_cur, arg_cur + 
sizeof(
"WARNING: ") - 1,
   808                                     strlen(arg_cur + 
sizeof(
"WARNING: ") - 1) + 1);
   813                         if (!strncmp(arg_cur, 
"INFO: ",
   814                                      sizeof(
"INFO: ") - 1)) {
   815                             msg_log_level = LOG_INFO;
   818                             memmove(arg_cur, arg_cur + 
sizeof(
"INFO: ") - 1,
   819                                     strlen(arg_cur + 
sizeof(
"INFO: ") - 1) + 1);
   824                         if (!strncmp(arg_cur, 
"DEBUG: ",
   825                                      sizeof(
"DEBUG: ") - 1)) {
   826                             msg_log_level = LOG_DEBUG;
   829                             memmove(arg_cur, arg_cur + 
sizeof(
"DEBUG: ") - 1,
   830                                     strlen(arg_cur + 
sizeof(
"DEBUG: ") - 1) + 1);
   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':
   857             if (scan_state == escan_seenpercent) {
   858                 (void) va_arg(aq, 
void *);  
   859                 scan_state = escan_seennothing;
   863             scan_state = escan_seennothing;
   868     if (log_level != NULL) {
   871         if (*log_level + 4 >= msg_log_level) {
   872             vfprintf(stderr, fmt, ap);
   897 #ifndef PCMK_SCHEMAS_EMERGENCY_XSLT   898 #define PCMK_SCHEMAS_EMERGENCY_XSLT 1   902 apply_transformation(xmlNode *xml, 
const char *transform, gboolean to_logs)
   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;
   920     xmlLoadExtDtdDefaultValue = 1;
   921     xmlSubstituteEntitiesDefault(1);
   925         xsltSetGenericErrorFunc(NULL, cib_upgrade_err);
   930     xslt = xsltParseStylesheetFile((
pcmkXmlStr) xform);
   933     res = xsltApplyStylesheet(xslt, doc, NULL);
   936     xsltSetGenericErrorFunc(NULL, NULL);  
   939 #if PCMK_SCHEMAS_EMERGENCY_XSLT != 0   940     emergency_res = xsltSaveResultToString(&emergency_result,
   941                                            &emergency_txt_len, res, xslt);
   943     CRM_CHECK(emergency_res == 0, 
goto cleanup);
   944     out = 
string2xml((
const char *) emergency_result);
   945     free(emergency_result);
   947     out = xmlDocGetRootElement(res);
   952         xsltFreeStylesheet(xslt);
   967 apply_upgrade(xmlNode *xml, 
const struct schema_s *schema, gboolean to_logs)
   969     bool transform_onleave = schema->transform_onleave;
   970     char *transform_leave;
   971     xmlNode *upgrade = NULL,
   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;
   984     if (upgrade == NULL) {
   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) {
   996     if (
final != NULL && transform_onleave) {
  1000         transform_leave = strdup(schema->transform_enter);
  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);
  1012         free(transform_leave);
  1023     if (version < 0 || version >= xml_schema_max) {
  1026     return known_schemas[
version].name;
  1037     for (; lpc < xml_schema_max; lpc++) {
  1050     xmlNode *xml = NULL;
  1052     int max_stable_schemas = xml_latest_schema_index();
  1056     CRM_CHECK(best != NULL, 
return -EINVAL);
  1059     CRM_CHECK(xml_blob != NULL, 
return -EINVAL);
  1060     CRM_CHECK(*xml_blob != NULL, 
return -EINVAL);
  1065     if (value != NULL) {
  1069         if (lpc >= 0 && transform == FALSE) {
  1072         } 
else if (lpc < 0) {
  1078     if (match >= max_stable_schemas) {
  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);
  1090         if (validate_with(xml, lpc, to_logs) == FALSE) {
  1092                 crm_info(
"Configuration not valid for schema: %s",
  1093                          known_schemas[lpc].
name);
  1097                           known_schemas[lpc].
name ? known_schemas[lpc].
name : 
"<unset>");
  1107                 crm_debug(
"Configuration valid for schema: %s",
  1108                           known_schemas[next].
name);
  1119             xmlNode *upgrade = NULL;
  1120             next = known_schemas[lpc].after_transform;
  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);
  1132             } 
else if (known_schemas[lpc].transform == NULL
  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);
  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);
  1150                 upgrade = apply_upgrade(xml, &known_schemas[lpc], to_logs);
  1152                 if (upgrade == NULL) {
  1153                     crm_err(
"Transformation %s.xsl failed",
  1154                             known_schemas[lpc].transform);
  1157                 } 
else if (validate_with(upgrade, next, to_logs)) {
  1158                     crm_info(
"Transformation %s.xsl successful",
  1159                              known_schemas[lpc].transform);
  1167                     crm_err(
"Transformation %s.xsl did not produce a valid configuration",
  1168                             known_schemas[lpc].transform);
  1177         if (transform == FALSE || 
rc != 
pcmk_ok) {
  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);
  1200     char *
const orig_value = strdup(value == NULL ? 
"(none)" : value);
  1204     int min_version = xml_minimum_schema_index();
  1208         xmlNode *converted = NULL;
  1217             if (
version < orig_version || orig_version == -1) {
  1221                                      "schema %s) to at least %s because it "  1222                                      "does not validate with any schema from "  1229                     fprintf(stderr, 
"Cannot upgrade configuration (claiming "  1230                                     "schema %s) to at least %s because it "  1231                                     "does not validate with any schema from "  1242                                      "schema %s) to at least %s because it "  1243                                      "would not upgrade past %s",
  1248                     fprintf(stderr, 
"Cannot upgrade configuration (claiming "  1249                                     "schema %s) to at least %s because it "  1250                                     "would not upgrade past %s\n",
  1266             if (
version < xml_latest_schema_index()) {
  1269                                       "internally upgraded to acceptable (but "  1270                                       "not most recent) %s",
  1275                     crm_info(
"Configuration with schema %s was internally "  1276                              "upgraded to latest version %s",
  1286                               "(enabling is encouraged and prevents common "  1287                               "misconfigurations)");
  1290             fprintf(stderr, 
"Schema validation of configuration is disabled "  1291                             "(enabling is encouraged and prevents common "  1292                             "misconfigurations)\n");
 char * pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
 
#define CRM_CHECK(expr, failure_action)
 
#define pcmk_err_schema_validation
 
void crm_schema_init(void)
 
#define crm_notice(fmt, args...)
 
#define schema_strdup_printf(prefix, version, suffix)
 
#define pcmk__config_warn(fmt...)
 
#define schema_scanf(s, prefix, version, suffix)
 
#define pcmk__config_err(fmt...)
 
#define pcmk_err_transform_failed
 
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value. 
 
char * strerror(int errnum)
 
xmlNode * string2xml(const char *input)
 
xmlDoc * getDocPtr(xmlNode *node)
 
xmlNode * copy_xml(xmlNode *src_node)
 
#define crm_warn(fmt, args...)
 
int get_schema_version(const char *name)
 
#define crm_debug(fmt, args...)
 
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute. 
 
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute. 
 
#define PCMK__XML_LOG_BASE(priority, dechunk, postemit, prefix, fmt, ap)
Base for directing lib{xml2,xslt} log into standard libqb backend. 
 
void crm_schema_cleanup(void)
 
#define crm_trace(fmt, args...)
 
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
 
const char * get_schema_name(int version)
 
Wrappers for and extensions to libxml2. 
 
#define XML_ATTR_VALIDATION
 
void free_xml(xmlNode *child)
 
gboolean validate_xml_verbose(xmlNode *xml_blob)
 
const xmlChar * pcmkXmlStr
 
gboolean validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
 
unsigned int crm_log_level
 
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr. 
 
#define crm_err(fmt, args...)
 
const char * pcmk__get_tmpdir(void)
 
#define crm_log_xml_info(xml, text)
 
gboolean cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs)
 
int update_validation(xmlNode **xml_blob, int *best, int max, gboolean transform, gboolean to_logs)
Update CIB XML to most recent schema version. 
 
int write_xml_fd(xmlNode *xml_node, const char *filename, int fd, gboolean compress)
Write XML to a file descriptor. 
 
char * pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, const char *filespec)
 
#define crm_info(fmt, args...)
 
bool pcmk__ends_with_ext(const char *s, const char *match)
 
const char * xml_latest_schema(void)