19 #include <libxml/relaxng.h>    20 #include <libxslt/xslt.h>    21 #include <libxslt/transform.h>    22 #include <libxslt/security.h>    23 #include <libxslt/xsltutils.h>    33 #define SCHEMA_ZERO { .v = { 0, 0 } }    35 #define schema_scanf(s, prefix, version, suffix) \    36     sscanf((s), prefix "%hhu.%hhu" suffix, &((version).v[0]), &((version).v[1]))    38 #define schema_strdup_printf(prefix, version, suffix) \    39     crm_strdup_printf(prefix "%u.%u" suffix, (version).v[0], (version).v[1])    43     xmlRelaxNGValidCtxtPtr valid;
    44     xmlRelaxNGParserCtxtPtr parser;
    45 } relaxng_ctx_cache_t;
    59     char *transform_enter;
    60     bool transform_onleave;
    63 static struct schema_s *known_schemas = NULL;
    64 static int xml_schema_max = 0;
    65 static bool silent_logging = FALSE;
    68 xml_log(
int priority, 
const char *fmt, ...)
    72 xml_log(
int priority, const 
char *fmt, ...)
    77     if (silent_logging == FALSE) {
    85 xml_latest_schema_index(
void)
    88     return xml_schema_max - 3; 
    92 xml_minimum_schema_index(
void)
    98         best = xml_latest_schema_index();
    99         for (lpc = best; lpc > 0; lpc--) {
   100             if (known_schemas[lpc].
version.v[0]
   101                 < known_schemas[best].version.v[0]) {
   107         best = xml_latest_schema_index();
   119 version_from_filename(
const char *filename, schema_version_t *
version)
   127 schema_filter(
const struct dirent *a)
   132     if (strstr(a->d_name, 
"pacemaker-") != a->d_name) {
   138     } 
else if (!version_from_filename(a->d_name, &
version)) {
   150 schema_sort(
const struct dirent **a, 
const struct dirent **b)
   155     if (!version_from_filename(a[0]->d_name, &a_version)
   156         || !version_from_filename(b[0]->d_name, &b_version)) {
   161     for (
int i = 0; i < 2; ++i) {
   162         if (a_version.v[i] < b_version.v[i]) {
   164         } 
else if (a_version.v[i] > b_version.v[i]) {
   180            const char *
name, 
const char *transform,
   181            const char *transform_enter, 
bool transform_onleave,
   184     int last = xml_schema_max;
   185     bool have_version = FALSE;
   188     known_schemas = pcmk__realloc(known_schemas,
   189                                   xml_schema_max * 
sizeof(
struct schema_s));
   191     memset(known_schemas+last, 0, 
sizeof(
struct schema_s));
   192     known_schemas[last].validator = validator;
   193     known_schemas[last].after_transform = after_transform;
   195     for (
int i = 0; i < 2; ++i) {
   196         known_schemas[last].version.v[i] = 
version->v[i];
   206         known_schemas[last].name = strdup(
name);
   210         known_schemas[last].transform = strdup(transform);
   212     if (transform_enter) {
   213         known_schemas[last].transform_enter = strdup(transform_enter);
   215     known_schemas[last].transform_onleave = transform_onleave;
   216     if (after_transform == 0) {
   217         after_transform = xml_schema_max;  
   219     known_schemas[last].after_transform = after_transform;
   221     if (known_schemas[last].after_transform < 0) {
   222         crm_debug(
"Added supported schema %d: %s",
   223                   last, known_schemas[last].
name);
   225     } 
else if (known_schemas[last].transform) {
   226         crm_debug(
"Added supported schema %d: %s (upgrades to %d with %s.xsl)",
   227                   last, known_schemas[last].
name,
   228                   known_schemas[last].after_transform,
   229                   known_schemas[last].transform);
   232         crm_debug(
"Added supported schema %d: %s (upgrades to %d)",
   233                   last, known_schemas[last].
name,
   234                   known_schemas[last].after_transform);
   267 add_schema_by_version(
const schema_version_t *
version, 
int next,
   268                       bool transform_expected)
   270     bool transform_onleave = FALSE;
   274          *transform_upgrade = NULL,
   275          *transform_enter = NULL;
   278     if (transform_expected) {
   285     if (!transform_expected) {
   288     } 
else if (stat(xslt, &s) == 0) {
   294         if (stat(xslt, &s) != 0) {
   296             crm_debug(
"Upgrade-enter transform %s.xsl not found", xslt);
   298             free(transform_enter);
   299             transform_enter = strdup(
"upgrade-enter");
   302             if (stat(xslt, &s) != 0) {
   303                 crm_debug(
"Upgrade-enter transform %s.xsl not found, either", xslt);
   311             memcpy(strrchr(xslt, 
'-') + 1, 
"leave", 
sizeof(
"leave") - 1);
   312             transform_onleave = (stat(xslt, &s) == 0);
   315             free(transform_enter);
   316             transform_enter = NULL;
   320         crm_err(
"Upgrade transform %s not found", xslt);
   322         free(transform_upgrade);
   323         transform_upgrade = NULL;
   329                transform_upgrade, transform_enter, transform_onleave, next);
   331     free(transform_upgrade);
   332     free(transform_enter);
   338 wrap_libxslt(
bool finalize)
   340     static xsltSecurityPrefsPtr secprefs;
   346         secprefs = xsltNewSecurityPrefs();
   347         ret = xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_WRITE_FILE,
   349               | xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_CREATE_DIRECTORY,
   351               | xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_READ_NETWORK,
   353               | xsltSetSecurityPrefs(secprefs, XSLT_SECPREF_WRITE_NETWORK,
   359         xsltFreeSecurityPrefs(secprefs);
   365         xsltCleanupGlobals();
   381     struct dirent **namelist = NULL;
   386     max = scandir(base, &namelist, schema_filter, schema_sort);
   393         for (lpc = 0; lpc < max; lpc++) {
   394             bool transform_expected = FALSE;
   398             if (!version_from_filename(namelist[lpc]->d_name, &
version)) {
   400                 crm_err(
"Skipping schema '%s': could not parse version",
   401                         namelist[lpc]->d_name);
   404             if ((lpc + 1) < max) {
   407                 if (version_from_filename(namelist[lpc+1]->d_name, &next_version)
   408                         && (
version.v[0] < next_version.v[0])) {
   409                     transform_expected = TRUE;
   415             if (add_schema_by_version(&
version, next, transform_expected)
   421         for (lpc = 0; lpc < max; lpc++) {
   429                NULL, NULL, FALSE, -1);
   432                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;
   609     if (pcmk__str_eq(known_schemas[method].
name, 
"pacemaker-next",
   611         crm_warn(
"The pacemaker-next schema is deprecated and will be removed "   612                  "in a future release.");
   617                                    known_schemas[method].
name);
   619     crm_trace(
"Validating with %s (type=%d)",
   620               pcmk__s(file, 
"missing schema"), known_schemas[method].validator);
   621     switch (known_schemas[method].validator) {
   624                 validate_with_relaxng(doc, to_logs, file,
   625                                       (relaxng_ctx_cache_t **) & (known_schemas[method].cache));
   628             crm_err(
"Unknown validator type: %d",
   629                     known_schemas[method].validator);
   638 validate_with_silent(xmlNode *xml, 
int method)
   640     bool rc, sl_backup = silent_logging;
   641     silent_logging = TRUE;
   642     rc = validate_with(xml, method, TRUE);
   643     silent_logging = sl_backup;
   648 dump_file(
const char *filename)
   656     fp = fopen(filename, 
"r");
   658         crm_perror(LOG_ERR, 
"Could not open %s for reading", filename);
   662     fprintf(stderr, 
"%4d ", ++line);
   668         } 
else if (ch == 
'\n') {
   669             fprintf(stderr, 
"\n%4d ", ++line);
   685     char *filename = NULL;
   689     umask(S_IWGRP | S_IWOTH | S_IROTH);
   690     fd = mkstemp(filename);
   695     doc = xmlParseFile(filename);
   696     xml = xmlDocGetRootElement(doc);
   707 validate_xml(xmlNode *xml_blob, 
const char *validation, gboolean to_logs)
   711     if (validation == NULL) {
   715     if (validation == NULL) {
   719         for (lpc = 0; lpc < xml_schema_max; lpc++) {
   720             if (validate_with(xml_blob, lpc, FALSE)) {
   723                             known_schemas[lpc].
name);
   724                 crm_info(
"XML validated against %s", known_schemas[lpc].
name);
   725                 if(known_schemas[lpc].after_transform == 0) {
   737     } 
else if (
version < xml_schema_max) {
   738         return validate_with(xml_blob, 
version, to_logs);
   741     crm_err(
"Unknown validator: %s", validation);
   746 cib_upgrade_err(
void *ctx, 
const char *fmt, ...)
   773 cib_upgrade_err(
void *ctx, const 
char *fmt, ...)
   779     const char *fmt_iter = fmt;
   780     uint8_t msg_log_level = LOG_WARNING;  
   781     const unsigned * log_level = (
const unsigned *) ctx;
   785     } scan_state = escan_seennothing;
   790     while (!found && *fmt_iter != 
'\0') {
   792         switch (*fmt_iter++) {
   794             if (scan_state == escan_seennothing) {
   795                 scan_state = escan_seenpercent;
   796             } 
else if (scan_state == escan_seenpercent) {
   797                 scan_state = escan_seennothing;
   801             if (scan_state == escan_seenpercent) {
   802                 scan_state = escan_seennothing;
   803                 arg_cur = va_arg(aq, 
char *);
   804                 if (arg_cur != NULL) {
   805                     switch (arg_cur[0]) {
   807                         if (!strncmp(arg_cur, 
"WARNING: ",
   808                                      sizeof(
"WARNING: ") - 1)) {
   809                             msg_log_level = LOG_WARNING;
   812                             memmove(arg_cur, arg_cur + 
sizeof(
"WARNING: ") - 1,
   813                                     strlen(arg_cur + 
sizeof(
"WARNING: ") - 1) + 1);
   818                         if (!strncmp(arg_cur, 
"INFO: ",
   819                                      sizeof(
"INFO: ") - 1)) {
   820                             msg_log_level = LOG_INFO;
   823                             memmove(arg_cur, arg_cur + 
sizeof(
"INFO: ") - 1,
   824                                     strlen(arg_cur + 
sizeof(
"INFO: ") - 1) + 1);
   829                         if (!strncmp(arg_cur, 
"DEBUG: ",
   830                                      sizeof(
"DEBUG: ") - 1)) {
   831                             msg_log_level = LOG_DEBUG;
   834                             memmove(arg_cur, arg_cur + 
sizeof(
"DEBUG: ") - 1,
   835                                     strlen(arg_cur + 
sizeof(
"DEBUG: ") - 1) + 1);
   843         case '#': 
case '-': 
case ' ': 
case '+': 
case '\'': 
case 'I': 
case '.':
   844         case '0': 
case '1': 
case '2': 
case '3': 
case '4':
   845         case '5': 
case '6': 
case '7': 
case '8': 
case '9':
   862             if (scan_state == escan_seenpercent) {
   863                 (void) va_arg(aq, 
void *);  
   864                 scan_state = escan_seennothing;
   868             scan_state = escan_seennothing;
   873     if (log_level != NULL) {
   876         if (*log_level + 4 >= msg_log_level) {
   877             vfprintf(stderr, fmt, ap);
   902 #ifndef PCMK_SCHEMAS_EMERGENCY_XSLT   903 #define PCMK_SCHEMAS_EMERGENCY_XSLT 1   907 apply_transformation(xmlNode *xml, 
const char *transform, gboolean to_logs)
   911     xmlDocPtr res = NULL;
   912     xmlDocPtr doc = NULL;
   913     xsltStylesheet *xslt = NULL;
   914 #if PCMK_SCHEMAS_EMERGENCY_XSLT != 0   915     xmlChar *emergency_result;
   916     int emergency_txt_len;
   925     xmlLoadExtDtdDefaultValue = 1;
   926     xmlSubstituteEntitiesDefault(1);
   930         xsltSetGenericErrorFunc(NULL, cib_upgrade_err);
   935     xslt = xsltParseStylesheetFile((
pcmkXmlStr) xform);
   938     res = xsltApplyStylesheet(xslt, doc, NULL);
   941     xsltSetGenericErrorFunc(NULL, NULL);  
   944 #if PCMK_SCHEMAS_EMERGENCY_XSLT != 0   945     emergency_res = xsltSaveResultToString(&emergency_result,
   946                                            &emergency_txt_len, res, xslt);
   948     CRM_CHECK(emergency_res == 0, 
goto cleanup);
   949     out = 
string2xml((
const char *) emergency_result);
   950     free(emergency_result);
   952     out = xmlDocGetRootElement(res);
   957         xsltFreeStylesheet(xslt);
   972 apply_upgrade(xmlNode *xml, 
const struct schema_s *schema, gboolean to_logs)
   974     bool transform_onleave = schema->transform_onleave;
   975     char *transform_leave;
   976     xmlNode *upgrade = NULL,
   979     if (schema->transform_enter) {
   980         crm_debug(
"Upgrading %s-style configuration, pre-upgrade phase with %s.xsl",
   981                   schema->name, schema->transform_enter);
   982         upgrade = apply_transformation(xml, schema->transform_enter, to_logs);
   983         if (upgrade == NULL) {
   984             crm_warn(
"Upgrade-enter transformation %s.xsl failed",
   985                      schema->transform_enter);
   986             transform_onleave = FALSE;
   989     if (upgrade == NULL) {
   993     crm_debug(
"Upgrading %s-style configuration, main phase with %s.xsl",
   994               schema->name, schema->transform);
   995     final = apply_transformation(upgrade, schema->transform, to_logs);
   996     if (upgrade != xml) {
  1001     if (
final != NULL && transform_onleave) {
  1005         transform_leave = strdup(schema->transform_enter);
  1007         memcpy(strrchr(transform_leave, 
'-') + 1, 
"leave", 
sizeof(
"leave") - 1);
  1008         crm_debug(
"Upgrading %s-style configuration, post-upgrade phase with %s.xsl",
  1009                   schema->name, transform_leave);
  1010         final = apply_transformation(upgrade, transform_leave, to_logs);
  1011         if (
final == NULL) {
  1012             crm_warn(
"Upgrade-leave transformation %s.xsl failed", transform_leave);
  1017         free(transform_leave);
  1026     if (version < 0 || version >= xml_schema_max) {
  1029     return known_schemas[
version].name;
  1040     for (; lpc < xml_schema_max; lpc++) {
  1053     xmlNode *xml = NULL;
  1055     int max_stable_schemas = xml_latest_schema_index();
  1056     int lpc = 0, match = -1, rc = 
pcmk_ok;
  1059     CRM_CHECK(best != NULL, 
return -EINVAL);
  1062     CRM_CHECK(xml_blob != NULL, 
return -EINVAL);
  1063     CRM_CHECK(*xml_blob != NULL, 
return -EINVAL);
  1068     if (value != NULL) {
  1072         if (lpc >= 0 && transform == FALSE) {
  1075         } 
else if (lpc < 0) {
  1081     if (match >= max_stable_schemas) {
  1088     while (lpc <= max_stable_schemas) {
  1089         crm_debug(
"Testing '%s' validation (%d of %d)",
  1090                   known_schemas[lpc].
name ? known_schemas[lpc].
name : 
"<unset>",
  1091                   lpc, max_stable_schemas);
  1093         if (validate_with(xml, lpc, to_logs) == FALSE) {
  1095                 crm_info(
"Configuration not valid for schema: %s",
  1096                          known_schemas[lpc].
name);
  1100                           known_schemas[lpc].
name ? known_schemas[lpc].
name : 
"<unset>");
  1110                 crm_debug(
"Configuration valid for schema: %s",
  1111                           known_schemas[next].
name);
  1121         if (rc == 
pcmk_ok && transform) {
  1122             xmlNode *upgrade = NULL;
  1123             next = known_schemas[lpc].after_transform;
  1130             } 
else if (max > 0 && (lpc == max || next > max)) {
  1131                 crm_trace(
"Upgrade limit reached at %s (lpc=%d, next=%d, max=%d)",
  1132                           known_schemas[lpc].
name, lpc, next, max);
  1135             } 
else if (known_schemas[lpc].transform == NULL
  1141                        || validate_with_silent(xml, next)) {
  1142                 crm_debug(
"%s-style configuration is also valid for %s",
  1143                            known_schemas[lpc].
name, known_schemas[next].
name);
  1148                 crm_debug(
"Upgrading %s-style configuration to %s with %s.xsl",
  1149                            known_schemas[lpc].
name, known_schemas[next].
name,
  1150                            known_schemas[lpc].transform);
  1152                 upgrade = apply_upgrade(xml, &known_schemas[lpc], to_logs);
  1153                 if (upgrade == NULL) {
  1154                     crm_err(
"Transformation %s.xsl failed",
  1155                             known_schemas[lpc].transform);
  1158                 } 
else if (validate_with(upgrade, next, to_logs)) {
  1159                     crm_info(
"Transformation %s.xsl successful",
  1160                              known_schemas[lpc].transform);
  1168                     crm_err(
"Transformation %s.xsl did not produce a valid configuration",
  1169                             known_schemas[lpc].transform);
  1178         if (transform == FALSE || rc != 
pcmk_ok) {
  1184     if (*best > match && *best) {
  1185         crm_info(
"%s the configuration from %s to %s",
  1186                    transform?
"Transformed":
"Upgraded",
  1187                    value ? value : 
"<none>", known_schemas[*best].
name);
  1201     char *
const orig_value = strdup(value == NULL ? 
"(none)" : value);
  1205     int min_version = xml_minimum_schema_index();
  1209         xmlNode *converted = NULL;
  1218             if (
version < orig_version || orig_version == -1) {
  1222                                      "schema %s) to at least %s because it "  1223                                      "does not validate with any schema from "  1230                     fprintf(stderr, 
"Cannot upgrade configuration (claiming "  1231                                     "schema %s) to at least %s because it "  1232                                     "does not validate with any schema from "  1243                                      "schema %s) to at least %s because it "  1244                                      "would not upgrade past %s",
  1247                                      pcmk__s(value, 
"unspecified version"));
  1249                     fprintf(stderr, 
"Cannot upgrade configuration (claiming "  1250                                     "schema %s) to at least %s because it "  1251                                     "would not upgrade past %s\n",
  1254                                     pcmk__s(value, 
"unspecified version"));
  1267             if (
version < xml_latest_schema_index()) {
  1270                                       "internally upgraded to acceptable (but "  1271                                       "not most recent) %s",
  1276                     crm_info(
"Configuration with schema %s was internally "  1277                              "upgraded to latest version %s",
  1287                               "(enabling is encouraged and prevents common "  1288                               "misconfigurations)");
  1291             fprintf(stderr, 
"Schema validation of configuration is disabled "  1292                             "(enabling is encouraged and prevents common "  1293                             "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
 
const char * pcmk__get_tmpdir(void)
 
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...)
 
#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)