diff --git a/include/pcmki/pcmki_acl.h b/include/pcmki/pcmki_acl.h index 33834c1228..26e1a7abbc 100644 --- a/include/pcmki/pcmki_acl.h +++ b/include/pcmki/pcmki_acl.h @@ -1,83 +1,74 @@ /* * Copyright 2004-2021 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #ifndef PCMK__PCMKI_PCMKI_ACL__H #define PCMK__PCMKI_PCMKI_ACL__H #include enum pcmk__acl_cred_type { pcmk__acl_cred_unset = 0, pcmk__acl_cred_user, /* XXX no proper support for groups yet */ }; enum pcmk__acl_render_how { pcmk__acl_render_ns_simple = 1, pcmk__acl_render_text, pcmk__acl_render_color, }; /* * Version compatibility tracking incl. open-ended intervals for occasional * bumps (to avoid hard to follow open-coding throughout). Grouped by context. */ /* Schema version vs. evaluate-as-namespace-annotations-per-credentials */ #define PCMK__COMPAT_ACL_2_MIN_INCL "pacemaker-2.0" /*! * \brief Mark CIB with namespace-encoded result of ACLs eval'd per credential * * \param[in] cred_type credential type that \p cred represents * \param[in] cred credential whose ACL perspective to switch to * \param[in] cib_doc XML document representing CIB * \param[out] acl_evaled_doc XML document representing CIB, with said * namespace-based annotations throughout * * \return A standard Pacemaker return code * Namely: * - pcmk_rc_ok upon success, * - pcmk_rc_already if ACLs were not applicable, * - pcmk_rc_schema_validation if the validation schema version * is unsupported (see note), or * - EINVAL or ENOMEM as appropriate; * * \note Only supported schemas are those following acls-2.0.rng, that is, * those validated with pacemaker-2.0.rng and newer. */ -int pcmk__acl_evaled_as_namespaces(const char *cred, xmlDoc *cib_doc, +int pcmk__acl_annotate_permissions(const char *cred, xmlDoc *cib_doc, xmlDoc **acl_evaled_doc); /*! * \internal - * \brief Serialize-render already pcmk__acl_evaled_as_namespaces annotated XML + * \brief Serialize-render already pcmk__acl_annotate_permissions annotated XML * - * This function is vitally coupled with externalized material: - * - access-render-2.xsl - * - * In fact, it's just a wrapper for a graceful conducting of such - * transformation, in particular, it cares about converting values of some - * configuration parameters directly in said stylesheet since the desired - * ANSI colors at the output are not expressible directly (alternative approach - * to this preprocessing: eventual postprocessing, which is less handy here). - * - * \param[in] annotated_doc pcmk__acl_evaled_as_namespaces annotated XML + * \param[in] annotated_doc pcmk__acl_annotate_permissions annotated XML * \param[in] how render kind, see #pcmk__acl_render_how enumeration * \param[out] doc_txt_ptr where to put the final outcome string * \return A standard Pacemaker return code * * \note Currently, the function did not receive enough of testing regarding * leak of resources, hence it is not recommended for anything other * than short-lived processes at this time. */ int pcmk__acl_evaled_render(xmlDoc *annotated_doc, enum pcmk__acl_render_how, xmlChar **doc_txt_ptr); #endif diff --git a/lib/pacemaker/pcmk_acl.c b/lib/pacemaker/pcmk_acl.c index 49bca756de..1e6758801a 100644 --- a/lib/pacemaker/pcmk_acl.c +++ b/lib/pacemaker/pcmk_acl.c @@ -1,322 +1,356 @@ /* * Copyright 2004-2022 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ACL_NS_PREFIX "http://clusterlabs.org/ns/pacemaker/access/" #define ACL_NS_Q_PREFIX "pcmk-access-" #define ACL_NS_Q_WRITABLE (const xmlChar *) ACL_NS_Q_PREFIX "writable" #define ACL_NS_Q_READABLE (const xmlChar *) ACL_NS_Q_PREFIX "readable" #define ACL_NS_Q_DENIED (const xmlChar *) ACL_NS_Q_PREFIX "denied" static const xmlChar *NS_WRITABLE = (const xmlChar *) ACL_NS_PREFIX "writable"; static const xmlChar *NS_READABLE = (const xmlChar *) ACL_NS_PREFIX "readable"; static const xmlChar *NS_DENIED = (const xmlChar *) ACL_NS_PREFIX "denied"; +/*! + * \brief This function takes a node and marks it with the namespace + * given in the ns parameter. + * + * \param[in,out] i_node + * \param[in] ns + * \param[in,out] ret + * \param[in,out] ns_recycle_writable + * \param[in,out] ns_recycle_readable + * \param[in,out] ns_recycle_denied + */ +static void +pcmk__acl_mark_node_with_namespace(xmlNode *i_node, const xmlChar *ns, int *ret, xmlNs **ns_recycle_writable, xmlNs **ns_recycle_readable, xmlNs **ns_recycle_denied) +{ + if (ns == NS_WRITABLE) + { + if (*ns_recycle_writable == NULL) + { + *ns_recycle_writable = xmlNewNs(xmlDocGetRootElement(i_node->doc), + NS_WRITABLE, ACL_NS_Q_WRITABLE); + } + xmlSetNs(i_node, *ns_recycle_writable); + *ret = pcmk_rc_ok; + } + else if (ns == NS_READABLE) + { + if (*ns_recycle_readable == NULL) + { + *ns_recycle_readable = xmlNewNs(xmlDocGetRootElement(i_node->doc), + NS_READABLE, ACL_NS_Q_READABLE); + } + xmlSetNs(i_node, *ns_recycle_readable); + *ret = pcmk_rc_ok; + } + else if (ns == NS_DENIED) + { + if (*ns_recycle_denied == NULL) + { + *ns_recycle_denied = xmlNewNs(xmlDocGetRootElement(i_node->doc), + NS_DENIED, ACL_NS_Q_DENIED); + }; + xmlSetNs(i_node, *ns_recycle_denied); + *ret = pcmk_rc_ok; + } +} + +/*! + * \brief This function takes some XML, and annotates it with XML + * namespaces to indicate the ACL permissions. + * + * \param[in,out] xml_modify + * + * \return A standard Pacemaker return code + * Namely: + * - pcmk_rc_ok upon success, + * - pcmk_rc_already if ACLs were not applicable, + * - pcmk_rc_schema_validation if the validation schema version + * is unsupported (see note), or + * - EINVAL or ENOMEM as appropriate; + */ static int -pcmk__eval_acl_as_namespaces_2(xmlNode *xml_modify) +pcmk__acl_annotate_permissions_recursive(xmlNode *xml_modify) { static xmlNs *ns_recycle_writable = NULL, *ns_recycle_readable = NULL, *ns_recycle_denied = NULL; static const xmlDoc *prev_doc = NULL; xmlNode *i_node = NULL; const xmlChar *ns; int ret = EINVAL; // nodes have not been processed yet if (prev_doc == NULL || prev_doc != xml_modify->doc) { prev_doc = xml_modify->doc; ns_recycle_writable = ns_recycle_readable = ns_recycle_denied = NULL; } for (i_node = xml_modify; i_node != NULL; i_node = i_node->next) { switch (i_node->type) { case XML_ELEMENT_NODE: pcmk__set_xml_doc_flag(i_node, pcmk__xf_tracking); if (!pcmk__check_acl(i_node, NULL, pcmk__xf_acl_read)) { ns = NS_DENIED; } else if (!pcmk__check_acl(i_node, NULL, pcmk__xf_acl_write)) { ns = NS_READABLE; } else { ns = NS_WRITABLE; } - if (ns == NS_WRITABLE) { - if (ns_recycle_writable == NULL) { - ns_recycle_writable = xmlNewNs(xmlDocGetRootElement(i_node->doc), - NS_WRITABLE, ACL_NS_Q_WRITABLE); - } - xmlSetNs(i_node, ns_recycle_writable); - ret = pcmk_rc_ok; - } else if (ns == NS_READABLE) { - if (ns_recycle_readable == NULL) { - ns_recycle_readable = xmlNewNs(xmlDocGetRootElement(i_node->doc), - NS_READABLE, ACL_NS_Q_READABLE); - } - xmlSetNs(i_node, ns_recycle_readable); - ret = pcmk_rc_ok; - } else if (ns == NS_DENIED) { - if (ns_recycle_denied == NULL) { - ns_recycle_denied = xmlNewNs(xmlDocGetRootElement(i_node->doc), - NS_DENIED, ACL_NS_Q_DENIED); - }; - xmlSetNs(i_node, ns_recycle_denied); - ret = pcmk_rc_ok; - } + pcmk__acl_mark_node_with_namespace(i_node, ns, &ret, &ns_recycle_writable, &ns_recycle_readable, &ns_recycle_denied); /* XXX recursion can be turned into plain iteration to save stack */ if (i_node->properties != NULL) { /* this is not entirely clear, but relies on the very same class-hierarchy emulation that libxml2 has firmly baked in its API/ABI */ - ret |= pcmk__eval_acl_as_namespaces_2((xmlNodePtr) i_node->properties); + ret |= pcmk__acl_annotate_permissions_recursive((xmlNodePtr) i_node->properties); } if (i_node->children != NULL) { - ret |= pcmk__eval_acl_as_namespaces_2(i_node->children); + ret |= pcmk__acl_annotate_permissions_recursive(i_node->children); } break; case XML_ATTRIBUTE_NODE: /* we can utilize that parent has already been assigned the ns */ if (!pcmk__check_acl(i_node->parent, (const char *) i_node->name, pcmk__xf_acl_read)) { ns = NS_DENIED; } else if (!pcmk__check_acl(i_node, (const char *) i_node->name, pcmk__xf_acl_write)) { ns = NS_READABLE; } else { ns = NS_WRITABLE; } - if (ns == NS_WRITABLE) { - if (ns_recycle_writable == NULL) { - ns_recycle_writable = xmlNewNs(xmlDocGetRootElement(i_node->doc), - NS_WRITABLE, ACL_NS_Q_WRITABLE); - } - xmlSetNs(i_node, ns_recycle_writable); - ret = pcmk_rc_ok; - } else if (ns == NS_READABLE) { - if (ns_recycle_readable == NULL) { - ns_recycle_readable = xmlNewNs(xmlDocGetRootElement(i_node->doc), - NS_READABLE, ACL_NS_Q_READABLE); - } - xmlSetNs(i_node, ns_recycle_readable); - ret = pcmk_rc_ok; - } else if (ns == NS_DENIED) { - if (ns_recycle_denied == NULL) { - ns_recycle_denied = xmlNewNs(xmlDocGetRootElement(i_node->doc), - NS_DENIED, ACL_NS_Q_DENIED); - } - xmlSetNs(i_node, ns_recycle_denied); - ret = pcmk_rc_ok; + pcmk__acl_mark_node_with_namespace(i_node, ns, &ret, &ns_recycle_writable, &ns_recycle_readable, &ns_recycle_denied); + break; + case XML_COMMENT_NODE: + /* we can utilize that parent has already been assigned the ns */ + if (!pcmk__check_acl(i_node->parent, (const char *) i_node->name, pcmk__xf_acl_read)) + { + ns = NS_DENIED; } + else if (!pcmk__check_acl(i_node->parent, (const char *) i_node->name, pcmk__xf_acl_write)) + { + ns = NS_READABLE; + } + else + { + ns = NS_WRITABLE; + } + pcmk__acl_mark_node_with_namespace(i_node, ns, &ret, &ns_recycle_writable, &ns_recycle_readable, &ns_recycle_denied); break; default: break; } } return ret; } int -pcmk__acl_evaled_as_namespaces(const char *cred, xmlDoc *cib_doc, +pcmk__acl_annotate_permissions(const char *cred, xmlDoc *cib_doc, xmlDoc **acl_evaled_doc) { int ret, version; xmlNode *target, *comment; const char *validation; CRM_CHECK(cred != NULL, return EINVAL); CRM_CHECK(cib_doc != NULL, return EINVAL); CRM_CHECK(acl_evaled_doc != NULL, return EINVAL); /* avoid trivial accidental XML injection */ if (strpbrk(cred, "<>&") != NULL) { return EINVAL; } if (!pcmk_acl_required(cred)) { /* nothing to evaluate */ return pcmk_rc_already; } - /* XXX see the comment for this function, pacemaker-4.0 may need - updating respectively in the future */ validation = crm_element_value(xmlDocGetRootElement(cib_doc), XML_ATTR_VALIDATION); version = get_schema_version(validation); if (get_schema_version(PCMK__COMPAT_ACL_2_MIN_INCL) > version) { return pcmk_rc_schema_validation; } target = copy_xml(xmlDocGetRootElement(cib_doc)); if (target == NULL) { return EINVAL; } pcmk__enable_acl(target, target, cred); - ret = pcmk__eval_acl_as_namespaces_2(target); /* XXX may need "switch" */ + ret = pcmk__acl_annotate_permissions_recursive(target); - if (ret > 0) { + if (ret == pcmk_rc_ok) { char* credentials = crm_strdup_printf("%s", cred); comment = xmlNewDocComment(target->doc, (pcmkXmlStr) credentials); free(credentials); if (comment == NULL) { xmlFreeNode(target); return EINVAL; } xmlAddPrevSibling(xmlDocGetRootElement(target->doc), comment); *acl_evaled_doc = target->doc; + return pcmk_rc_ok; } else { xmlFreeNode(target); + return ret; //for now, it should be some kind of error } - return pcmk_rc_ok; } int pcmk__acl_evaled_render(xmlDoc *annotated_doc, enum pcmk__acl_render_how how, xmlChar **doc_txt_ptr) { xmlDoc *xslt_doc; xsltStylesheet *xslt; xsltTransformContext *xslt_ctxt; xmlDoc *res; char *sfile; static const char *params_ns_simple[] = { "accessrendercfg:c-writable", ACL_NS_Q_PREFIX "writable:", "accessrendercfg:c-readable", ACL_NS_Q_PREFIX "readable:", "accessrendercfg:c-denied", ACL_NS_Q_PREFIX "denied:", "accessrendercfg:c-reset", "", "accessrender:extra-spacing", "no", "accessrender:self-reproducing-prefix", ACL_NS_Q_PREFIX, NULL }, *params_useansi[] = { /* start with hard-coded defaults, then adapt per the template ones */ "accessrendercfg:c-writable", "\x1b[32m", "accessrendercfg:c-readable", "\x1b[34m", "accessrendercfg:c-denied", "\x1b[31m", "accessrendercfg:c-reset", "\x1b[0m", "accessrender:extra-spacing", "no", "accessrender:self-reproducing-prefix", ACL_NS_Q_PREFIX, NULL }, *params_noansi[] = { "accessrendercfg:c-writable", "vvv---[ WRITABLE ]---vvv", "accessrendercfg:c-readable", "vvv---[ READABLE ]---vvv", "accessrendercfg:c-denied", "vvv---[ ~DENIED~ ]---vvv", "accessrendercfg:c-reset", "", "accessrender:extra-spacing", "yes", "accessrender:self-reproducing-prefix", "", NULL }; const char **params; int ret; xmlParserCtxtPtr parser_ctxt; /* unfortunately, the input (coming from CIB originally) was parsed with blanks ignored, and since the output is a conversion of XML to text format (we would be covered otherwise thanks to implicit pretty-printing), we need to dump the tree to string output first, only to subsequently reparse it -- this time with blanks honoured */ xmlChar *annotated_dump; int dump_size; + xmlDocDumpFormatMemory(annotated_doc, &annotated_dump, &dump_size, 1); res = xmlReadDoc(annotated_dump, "on-the-fly-access-render", NULL, XML_PARSE_NONET); CRM_ASSERT(res != NULL); xmlFree(annotated_dump); xmlFreeDoc(annotated_doc); annotated_doc = res; sfile = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_base_xslt, "access-render-2"); parser_ctxt = xmlNewParserCtxt(); CRM_ASSERT(sfile != NULL); CRM_ASSERT(parser_ctxt != NULL); xslt_doc = xmlCtxtReadFile(parser_ctxt, sfile, NULL, XML_PARSE_NONET); xslt = xsltParseStylesheetDoc(xslt_doc); /* acquires xslt_doc! */ if (xslt == NULL) { crm_crit("Problem in parsing %s", sfile); return EINVAL; } free(sfile); sfile = NULL; xmlFreeParserCtxt(parser_ctxt); xslt_ctxt = xsltNewTransformContext(xslt, annotated_doc); CRM_ASSERT(xslt_ctxt != NULL); if (how == pcmk__acl_render_ns_simple) { params = params_ns_simple; } else if (how == pcmk__acl_render_text) { params = params_noansi; } else { params = params_useansi; } xsltQuoteUserParams(xslt_ctxt, params); res = xsltApplyStylesheetUser(xslt, annotated_doc, NULL, NULL, NULL, xslt_ctxt); xmlFreeDoc(annotated_doc); annotated_doc = NULL; xsltFreeTransformContext(xslt_ctxt); xslt_ctxt = NULL; if (how == pcmk__acl_render_color && params != params_useansi) { char **param_i = (char **) params; do { free(*param_i); } while (*param_i++ != NULL); free(params); } if (res == NULL) { ret = EINVAL; } else { int doc_txt_len; int temp = xsltSaveResultToString(doc_txt_ptr, &doc_txt_len, res, xslt); xmlFreeDoc(res); if (temp == 0) { ret = pcmk_rc_ok; } else { ret = EINVAL; } } xsltFreeStylesheet(xslt); return ret; } diff --git a/tools/Makefile.am b/tools/Makefile.am index e4385da6e0..2e09e8527b 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,172 +1,173 @@ # -# Copyright 2004-2021 the Pacemaker project contributors +# Copyright 2004-2022 the Pacemaker project contributors # # The version control history for this file may have further details. # # This source code is licensed under the GNU General Public License version 2 # or later (GPLv2+) WITHOUT ANY WARRANTY. # include $(top_srcdir)/mk/common.mk include $(top_srcdir)/mk/man.mk if BUILD_SYSTEMD systemdsystemunit_DATA = crm_mon.service endif noinst_HEADERS = crm_mon.h crm_resource.h pcmkdir = $(datadir)/$(PACKAGE) pcmk_DATA = report.common report.collector sbin_SCRIPTS = crm_report crm_standby crm_master crm_failcount if BUILD_CIBSECRETS sbin_SCRIPTS += cibsecret endif noinst_SCRIPTS = pcmk_simtimes EXTRA_DIST = crm_attribute.8.inc \ crm_diff.8.inc \ crm_error.8.inc \ crm_mon.8.inc \ crm_node.8.inc \ crm_resource.8.inc \ crm_rule.8.inc \ crm_simulate.8.inc \ crm_verify.8.inc \ crmadmin.8.inc \ fix-manpages \ stonith_admin.8.inc sbin_PROGRAMS = attrd_updater \ cibadmin \ crmadmin \ crm_simulate \ crm_attribute \ crm_diff \ crm_error \ crm_mon \ crm_node \ crm_resource \ crm_rule \ crm_shadow \ crm_verify \ crm_ticket \ iso8601 \ stonith_admin if BUILD_SERVICELOG sbin_PROGRAMS += notifyServicelogEvent endif if BUILD_OPENIPMI_SERVICELOG sbin_PROGRAMS += ipmiservicelogd endif ## SOURCES # A few tools are just thin wrappers around crm_attribute. # This makes their help get updated when crm_attribute changes # (see mk/common.mk). MAN8DEPS = crm_attribute crmadmin_SOURCES = crmadmin.c crmadmin_LDADD = $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la \ $(top_builddir)/lib/pacemaker/libpacemaker.la crm_error_SOURCES = crm_error.c crm_error_LDADD = $(top_builddir)/lib/common/libcrmcommon.la cibadmin_SOURCES = cibadmin.c -cibadmin_LDADD = $(top_builddir)/lib/cib/libcib.la \ - $(top_builddir)/lib/common/libcrmcommon.la +cibadmin_LDADD = $(top_builddir)/lib/pacemaker/libpacemaker.la \ + $(top_builddir)/lib/cib/libcib.la \ + $(top_builddir)/lib/common/libcrmcommon.la crm_shadow_SOURCES = crm_shadow.c crm_shadow_LDADD = $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_node_SOURCES = crm_node.c crm_node_LDADD = $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_simulate_SOURCES = crm_simulate.c crm_simulate_LDADD = $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_diff_SOURCES = crm_diff.c crm_diff_LDADD = $(top_builddir)/lib/common/libcrmcommon.la crm_mon_SOURCES = crm_mon.c crm_mon_curses.c crm_mon_LDADD = $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/fencing/libstonithd.la \ $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la \ $(CURSESLIBS) crm_verify_SOURCES = crm_verify.c crm_verify_LDADD = $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_attribute_SOURCES = crm_attribute.c crm_attribute_LDADD = $(top_builddir)/lib/cluster/libcrmcluster.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_resource_SOURCES = crm_resource.c \ crm_resource_ban.c \ crm_resource_print.c \ crm_resource_runtime.c crm_resource_LDADD = $(top_builddir)/lib/pengine/libpe_rules.la \ $(top_builddir)/lib/fencing/libstonithd.la \ $(top_builddir)/lib/lrmd/liblrmd.la \ $(top_builddir)/lib/services/libcrmservice.la \ $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_rule_SOURCES = crm_rule.c crm_rule_LDADD = $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/pengine/libpe_rules.la \ $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/common/libcrmcommon.la iso8601_SOURCES = iso8601.c iso8601_LDADD = $(top_builddir)/lib/common/libcrmcommon.la attrd_updater_SOURCES = attrd_updater.c attrd_updater_LDADD = $(top_builddir)/lib/common/libcrmcommon.la crm_ticket_SOURCES = crm_ticket.c crm_ticket_LDADD = $(top_builddir)/lib/pengine/libpe_rules.la \ $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la stonith_admin_SOURCES = stonith_admin.c stonith_admin_LDADD = $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/fencing/libstonithd.la \ $(top_builddir)/lib/common/libcrmcommon.la if BUILD_SERVICELOG notifyServicelogEvent_SOURCES = notifyServicelogEvent.c notifyServicelogEvent_CFLAGS = $(SERVICELOG_CFLAGS) notifyServicelogEvent_LDADD = $(top_builddir)/lib/common/libcrmcommon.la $(SERVICELOG_LIBS) endif if BUILD_OPENIPMI_SERVICELOG ipmiservicelogd_SOURCES = ipmiservicelogd.c ipmiservicelogd_CFLAGS = $(OPENIPMI_SERVICELOG_CFLAGS) $(SERVICELOG_CFLAGS) ipmiservicelogd_LDFLAGS = $(top_builddir)/lib/common/libcrmcommon.la $(OPENIPMI_SERVICELOG_LIBS) $(SERVICELOG_LIBS) endif CLEANFILES = $(man8_MANS) diff --git a/tools/cibadmin.c b/tools/cibadmin.c index bf62ffdcb9..1e28a412c7 100644 --- a/tools/cibadmin.c +++ b/tools/cibadmin.c @@ -1,938 +1,921 @@ /* * Copyright 2004-2022 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include -#define local_barf(...) \ - do { (void)(fprintf(stderr, __VA_ARGS__) > 0 && fflush(stderr)); } while (0) -#define local_const_barf(_s) local_barf("%s\n", _s); +#include static int message_timeout_ms = 30; static int command_options = 0; static int request_id = 0; static int bump_log_num = 0; static char *host = NULL; static const char *cib_user = NULL; static const char *cib_action = NULL; static const char *obj_type = NULL; static cib_t *the_cib = NULL; static GMainLoop *mainloop = NULL; static gboolean force_flag = FALSE; static crm_exit_t exit_code = CRM_EX_OK; int do_init(void); int do_work(xmlNode *input, int command_options, xmlNode **output); void cibadmin_op_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data); static pcmk__cli_option_t long_options[] = { // long option, argument type, storage, short option, description, flags { "help", no_argument, NULL, '?', "\tThis text", pcmk__option_default }, { "version", no_argument, NULL, '$', "\tVersion information", pcmk__option_default }, { "verbose", no_argument, NULL, 'V', "\tIncrease debug output\n", pcmk__option_default }, { "-spacer-", no_argument, NULL, '-', "Commands:", pcmk__option_default }, { "upgrade", no_argument, NULL, 'u', "\tUpgrade the configuration to the latest syntax", pcmk__option_default }, { "query", no_argument, NULL, 'Q', "\tQuery the contents of the CIB", pcmk__option_default }, { "erase", no_argument, NULL, 'E', "\tErase the contents of the whole CIB", pcmk__option_default }, { "bump", no_argument, NULL, 'B', "\tIncrease the CIB's epoch value by 1", pcmk__option_default }, { "create", no_argument, NULL, 'C', "\tCreate an object in the CIB (will fail if object already exists)", pcmk__option_default }, { "modify", no_argument, NULL, 'M', "\tFind object somewhere in CIB's XML tree and update it " "(fails if object does not exist unless -c is also specified)", pcmk__option_default }, { "patch", no_argument, NULL, 'P', "\tSupply an update in the form of an XML diff (see crm_diff(8))", pcmk__option_default }, { "replace", no_argument, NULL, 'R', "\tRecursively replace an object in the CIB", pcmk__option_default }, { "delete", no_argument, NULL, 'D', "\tDelete first object matching supplied criteria " "(for example, )", pcmk__option_default }, { "-spacer-", no_argument, NULL, '-', "\n\tThe XML element name and all attributes must match " "in order for the element to be deleted.\n", pcmk__option_default }, { "delete-all", no_argument, NULL, 'd', "When used with --xpath, remove all matching objects in the " "configuration instead of just the first one", pcmk__option_default }, { "empty", no_argument, NULL, 'a', "\tOutput an empty CIB", pcmk__option_default }, { "md5-sum", no_argument, NULL, '5', "\tCalculate the on-disk CIB digest", pcmk__option_default }, { "md5-sum-versioned", no_argument, NULL, '6', "Calculate an on-the-wire versioned CIB digest", pcmk__option_default }, -#if ENABLE_ACL { - "access-render", optional_argument, NULL, 'S', - "Like -Q + evaluate access for --user specified " - "user, presented in particular way", + "show-access", optional_argument, NULL, 'S', + "Whether to use syntax highlighting for ACLs " + "(with -Q/--query and -U/--user)", pcmk__option_default }, { "-spacer-", no_argument, NULL, '-', "\n\tThat amounts to one of \"color\" (default for terminal)," " \"text\" (otherwise), \"ns-full\", \"ns-simple\", or \"auto\"" " (per former defaults).", pcmk__option_default }, - { - "-spacer-", no_argument, NULL, '-', - "\n\tParticular appearance for \"color\" can be administratively modified in" - " /etc/pacemaker/access-render.cfg.xsl file (or equivalent), which can also" - " be overridden per user in $XDG_CONFIG_HOME/pacemaker/access-render.cfg.xsl," - " where $XDG_CONFIG_HOME is to be understood as ~/.config by default.\n", - pcmk__option_default - }, -#endif { "blank", no_argument, NULL, '-', NULL, pcmk__option_hidden }, { "-spacer-", required_argument, NULL, '-', "\nAdditional options:", pcmk__option_default }, { "force", no_argument, NULL, 'f', NULL, pcmk__option_default }, { "timeout", required_argument, NULL, 't', "Time (in seconds) to wait before declaring the operation failed", pcmk__option_default }, { "user", required_argument, NULL, 'U', "Run the command with permissions of the named user (valid only for " "the root and " CRM_DAEMON_USER " accounts)", pcmk__option_default }, { "sync-call", no_argument, NULL, 's', "Wait for call to complete before returning", pcmk__option_default }, { "local", no_argument, NULL, 'l', "\tCommand takes effect locally (should be used only for queries)", pcmk__option_default }, { "allow-create", no_argument, NULL, 'c', "(Advanced) Allow target of --modify/-M to be created " "if it does not exist", pcmk__option_default }, { "no-children", no_argument, NULL, 'n', "(Advanced) When querying an object, do not include its children " "in the result", pcmk__option_default }, { "no-bcast", no_argument, NULL, 'b', NULL, pcmk__option_hidden }, { "-spacer-", no_argument, NULL, '-', "\nData:", pcmk__option_default }, { "xml-text", required_argument, NULL, 'X', "Retrieve XML from the supplied string", pcmk__option_default }, { "xml-file", required_argument, NULL, 'x', "Retrieve XML from the named file", pcmk__option_default }, { "xml-pipe", no_argument, NULL, 'p', "Retrieve XML from stdin\n", pcmk__option_default }, { "scope", required_argument, NULL, 'o', "Limit scope of operation to specific section of CIB", pcmk__option_default }, { "-spacer-", no_argument, NULL, '-', "\tValid values: configuration, nodes, resources, constraints, " "crm_config, rsc_defaults, op_defaults, acls, fencing-topology, " "tags, alerts", pcmk__option_default }, { "xpath", required_argument, NULL, 'A', "A valid XPath to use instead of --scope/-o", pcmk__option_default }, { "node-path", no_argument, NULL, 'e', "When performing XPath queries, return path of any matches found", pcmk__option_default }, { "-spacer-", no_argument, NULL, '-', "\t(for example, \"/cib/configuration/resources/clone[@id='ms_RH1_SCS']" "/primitive[@id='prm_RH1_SCS']\")", pcmk__option_paragraph }, { "node", required_argument, NULL, 'N', "(Advanced) Send command to the specified host", pcmk__option_default }, { "-spacer-", no_argument, NULL, '!', NULL, pcmk__option_hidden }, { "-spacer-", no_argument, NULL, '-', "\n\nExamples:\n", pcmk__option_default }, { "-spacer-", no_argument, NULL, '-', "Query the configuration from the local node:", pcmk__option_paragraph }, { "-spacer-", no_argument, NULL, '-', " cibadmin --query --local", pcmk__option_example }, { "-spacer-", no_argument, NULL, '-', "Query just the cluster options configuration:", pcmk__option_paragraph }, { "-spacer-", no_argument, NULL, '-', " cibadmin --query --scope crm_config", pcmk__option_example }, { "-spacer-", no_argument, NULL, '-', "Query all 'target-role' settings:", pcmk__option_paragraph }, { "-spacer-", no_argument, NULL, '-', " cibadmin --query --xpath \"//nvpair[@name='target-role']\"", pcmk__option_example }, { "-spacer-", no_argument, NULL, '-', "Remove all 'is-managed' settings:", pcmk__option_paragraph }, { "-spacer-", no_argument, NULL, '-', " cibadmin --delete-all --xpath \"//nvpair[@name='is-managed']\"", pcmk__option_example }, { "-spacer-", no_argument, NULL, '-', "Remove the resource named 'old':", pcmk__option_paragraph }, { "-spacer-", no_argument, NULL, '-', " cibadmin --delete --xml-text ''", pcmk__option_example }, { "-spacer-", no_argument, NULL, '-', "Remove all resources from the configuration:", pcmk__option_paragraph }, { "-spacer-", no_argument, NULL, '-', " cibadmin --replace --scope resources --xml-text ''", pcmk__option_example }, { "-spacer-", no_argument, NULL, '-', "Replace complete configuration with contents of $HOME/pacemaker.xml:", pcmk__option_paragraph }, { "-spacer-", no_argument, NULL, '-', " cibadmin --replace --xml-file $HOME/pacemaker.xml", pcmk__option_example }, { "-spacer-", no_argument, NULL, '-', "Replace constraints section of configuration with contents of " "$HOME/constraints.xml:", pcmk__option_paragraph }, { "-spacer-", no_argument, NULL, '-', " cibadmin --replace --scope constraints --xml-file " "$HOME/constraints.xml", pcmk__option_example }, { "-spacer-", no_argument, NULL, '-', "Increase configuration version to prevent old configurations from " "being loaded accidentally:", pcmk__option_paragraph }, { "-spacer-", no_argument, NULL, '-', " cibadmin --modify --xml-text ''", pcmk__option_example }, { "-spacer-", no_argument, NULL, '-', "Edit the configuration with your favorite $EDITOR:", pcmk__option_paragraph }, { "-spacer-", no_argument, NULL, '-', " cibadmin --query > $HOME/local.xml", pcmk__option_example }, { "-spacer-", no_argument, NULL, '-', " $EDITOR $HOME/local.xml", pcmk__option_example }, { "-spacer-", no_argument, NULL, '-', " cibadmin --replace --xml-file $HOME/local.xml", pcmk__option_example }, -#if ENABLE_ACL { "-spacer-", no_argument, NULL, '-', - "Render configuration in color (assuming terminal) visualizing access as evaluated for user tony:", - pcmk_option_paragraph + "Assuming terminal, render configuration in color (green for writable, blue for readable, red for denied) to visualize permissions for user tony:", + pcmk__option_paragraph }, { "-spacer-", no_argument, NULL, '-', - " cibadmin --access-render --user tony", - pcmk_option_example + " cibadmin --show-access=color --query --user tony | less -r", + pcmk__option_example }, -#endif { "-spacer-", no_argument, NULL, '-', "SEE ALSO:", pcmk__option_default }, { "-spacer-", no_argument, NULL, '-', " crm(8), pcs(8), crm_shadow(8), crm_diff(8)", pcmk__option_default }, { "host", required_argument, NULL, 'h', "deprecated", pcmk__option_hidden }, { 0, 0, 0, 0 } }; static void print_xml_output(xmlNode * xml) { char *buffer; if (!xml) { return; } else if (xml->type != XML_ELEMENT_NODE) { return; } if (command_options & cib_xpath_address) { const char *id = crm_element_value(xml, XML_ATTR_ID); if (pcmk__str_eq((const char *)xml->name, "xpath-query", pcmk__str_casei)) { xmlNode *child = NULL; for (child = xml->children; child; child = child->next) { print_xml_output(child); } } else if (id) { printf("%s\n", id); } } else { buffer = dump_xml_formatted(xml); fprintf(stdout, "%s", crm_str(buffer)); free(buffer); } } // Upgrade requested but already at latest schema static void report_schema_unchanged(void) { const char *err = pcmk_rc_str(pcmk_rc_schema_unchanged); crm_info("Upgrade unnecessary: %s\n", err); printf("Upgrade unnecessary: %s\n", err); exit_code = CRM_EX_OK; } int main(int argc, char **argv) { int argerr = 0; int rc = pcmk_ok; int flag; const char *source = NULL; const char *admin_input_xml = NULL; const char *admin_input_file = NULL; gboolean dangerous_cmd = FALSE; gboolean admin_input_stdin = FALSE; xmlNode *output = NULL; xmlNode *input = NULL; -#if ENABLE_ACL char *username = NULL; const char *acl_cred = NULL; enum acl_eval_how { acl_eval_unused, acl_eval_auto, acl_eval_ns_full, acl_eval_ns_simple, acl_eval_text, acl_eval_color, } acl_eval_how = acl_eval_unused; -#endif int option_index = 0; pcmk__cli_init_logging("cibadmin", 0); set_crm_log_level(LOG_CRIT); pcmk__set_cli_options(NULL, " [options]", long_options, "query and edit the Pacemaker configuration"); if (argc < 2) { pcmk__cli_help('?', CRM_EX_USAGE); } while (1) { flag = pcmk__next_cli_option(argc, argv, &option_index, NULL); if (flag == -1) break; switch (flag) { case 't': message_timeout_ms = atoi(optarg); if (message_timeout_ms < 1) { message_timeout_ms = 30; } break; case 'A': obj_type = optarg; cib__set_call_options(command_options, crm_system_name, cib_xpath); break; case 'e': cib__set_call_options(command_options, crm_system_name, cib_xpath_address); break; case 'u': cib_action = CIB_OP_UPGRADE; dangerous_cmd = TRUE; break; case 'E': cib_action = CIB_OP_ERASE; dangerous_cmd = TRUE; break; -#if ENABLE_ACL case 'S': if (optarg != NULL) { if (!strcmp(optarg, "auto")) { acl_eval_how = acl_eval_auto; } else if (!strcmp(optarg, "ns-full")) { acl_eval_how = acl_eval_ns_full; } else if (!strcmp(optarg, "ns-simple")) { acl_eval_how = acl_eval_ns_simple; } else if (!strcmp(optarg, "text")) { acl_eval_how = acl_eval_text; } else if (!strcmp(optarg, "color")) { acl_eval_how = acl_eval_color; } else { - printf("Unrecognized option for --access-render: \"%s\"\n", + fprintf(stderr, "Unrecognized value for --show-access: \"%s\"\n", optarg); ++argerr; } } else { acl_eval_how = acl_eval_auto; } /* XXX this is a workaround until we unify happy paths for both a/sync handling; the respective extra code is only in sync path now, but does it matter at all for query-like request wrt. what blackbox users observe? */ command_options |= cib_sync_call; - /* fall-through */ -#endif + break; case 'Q': cib_action = CIB_OP_QUERY; break; case 'P': cib_action = CIB_OP_APPLY_DIFF; break; case 'U': cib_user = optarg; break; case 'M': cib_action = CIB_OP_MODIFY; break; case 'R': cib_action = CIB_OP_REPLACE; break; case 'C': cib_action = CIB_OP_CREATE; break; case 'D': cib_action = CIB_OP_DELETE; break; case '5': cib_action = "md5-sum"; break; case '6': cib_action = "md5-sum-versioned"; break; case 'c': cib__set_call_options(command_options, crm_system_name, cib_can_create); break; case 'n': cib__set_call_options(command_options, crm_system_name, cib_no_children); break; case 'B': cib_action = CIB_OP_BUMP; crm_log_args(argc, argv); break; case 'V': cib__set_call_options(command_options, crm_system_name, cib_verbose); bump_log_num++; break; case '?': case '$': case '!': pcmk__cli_help(flag, CRM_EX_OK); break; case 'o': crm_trace("Option %c => %s", flag, optarg); obj_type = optarg; break; case 'X': crm_trace("Option %c => %s", flag, optarg); admin_input_xml = optarg; crm_log_args(argc, argv); break; case 'x': crm_trace("Option %c => %s", flag, optarg); admin_input_file = optarg; crm_log_args(argc, argv); break; case 'p': admin_input_stdin = TRUE; crm_log_args(argc, argv); break; case 'N': case 'h': pcmk__str_update(&host, optarg); break; case 'l': cib__set_call_options(command_options, crm_system_name, cib_scope_local); break; case 'd': cib_action = CIB_OP_DELETE; cib__set_call_options(command_options, crm_system_name, cib_multiple); dangerous_cmd = TRUE; break; case 'b': dangerous_cmd = TRUE; cib__set_call_options(command_options, crm_system_name, cib_inhibit_bcast|cib_scope_local); break; case 's': cib__set_call_options(command_options, crm_system_name, cib_sync_call); break; case 'f': force_flag = TRUE; cib__set_call_options(command_options, crm_system_name, cib_quorum_override); crm_log_args(argc, argv); break; case 'a': output = createEmptyCib(1); if (optind < argc) { crm_xml_add(output, XML_ATTR_VALIDATION, argv[optind]); } admin_input_xml = dump_xml_formatted(output); fprintf(stdout, "%s\n", crm_str(admin_input_xml)); crm_exit(CRM_EX_OK); break; default: printf("Argument code 0%o (%c)" " is not (?yet?) supported\n", flag, flag); ++argerr; break; } } while (bump_log_num > 0) { crm_bump_log_level(argc, argv); bump_log_num--; } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) printf("%s ", argv[optind++]); printf("\n"); pcmk__cli_help('?', CRM_EX_USAGE); } if (optind > argc || cib_action == NULL) { ++argerr; } if (argerr) { pcmk__cli_help('?', CRM_EX_USAGE); } if (dangerous_cmd && force_flag == FALSE) { fprintf(stderr, "The supplied command is considered dangerous." " To prevent accidental destruction of the cluster," " the --force flag is required in order to proceed.\n"); fflush(stderr); crm_exit(CRM_EX_UNSAFE); } if (admin_input_file != NULL) { input = filename2xml(admin_input_file); source = admin_input_file; } else if (admin_input_xml != NULL) { source = "input string"; input = string2xml(admin_input_xml); } else if (admin_input_stdin) { source = "STDIN"; input = stdin2xml(); -#if ENABLE_ACL } else if (acl_eval_how != acl_eval_unused) { - username = uid2username(geteuid()); + username = pcmk__uid2username(geteuid()); if (pcmk_acl_required(username)) { - if (force_flag == TRUE) { - local_const_barf("The supplied command can provide skewed" + if (force_flag) { + fprintf(stderr, "The supplied command can provide skewed" " result since it is run under user that also" " gets guarded per ACLs on their own right." " Continuing since --force flag was" - " provided."); + " provided.\n"); } else { - local_const_barf("The supplied command can provide skewed" + fprintf(stderr, "The supplied command can provide skewed" " result since it is run under user that also" " gets guarded per ACLs in their own right." " To accept the risk of such a possible" " distortion (without even knowing it at this" - " time), use the --force flag."); + " time), use the --force flag.\n"); crm_exit(CRM_EX_UNSAFE); } } free(username); username = NULL; if (cib_user == NULL) { - local_const_barf("The supplied command requires -U user specified."); + fprintf(stderr, "The supplied command requires -U user specified.\n"); crm_exit(CRM_EX_USAGE); } /* we already stopped/warned ACL-controlled users about consequences */ acl_cred = cib_user; - cib_user = NULL; /* XXX CRM_DAEMON_USER as it's IPC guarded anyway? */ - /* XXX should likely differenciate per admin_input_* */ -#endif + cib_user = NULL; } if (input != NULL) { crm_log_xml_debug(input, "[admin input]"); } else if (source) { fprintf(stderr, "Couldn't parse input from %s.\n", source); crm_exit(CRM_EX_CONFIG); } if (pcmk__str_eq(cib_action, "md5-sum", pcmk__str_casei)) { char *digest = NULL; if (input == NULL) { fprintf(stderr, "Please supply XML to process with -X, -x or -p\n"); crm_exit(CRM_EX_USAGE); } digest = calculate_on_disk_digest(input); fprintf(stderr, "Digest: "); fprintf(stdout, "%s\n", crm_str(digest)); free(digest); free_xml(input); crm_exit(CRM_EX_OK); } else if (pcmk__str_eq(cib_action, "md5-sum-versioned", pcmk__str_casei)) { char *digest = NULL; const char *version = NULL; if (input == NULL) { fprintf(stderr, "Please supply XML to process with -X, -x or -p\n"); crm_exit(CRM_EX_USAGE); } version = crm_element_value(input, XML_ATTR_CRM_VERSION); digest = calculate_xml_versioned_digest(input, FALSE, TRUE, version); fprintf(stderr, "Versioned (%s) digest: ", version); fprintf(stdout, "%s\n", crm_str(digest)); free(digest); free_xml(input); crm_exit(CRM_EX_OK); } rc = do_init(); if (rc != pcmk_ok) { crm_err("Init failed, could not perform requested operations"); fprintf(stderr, "Init failed, could not perform requested operations\n"); free_xml(input); crm_exit(crm_errno2exit(rc)); } rc = do_work(input, command_options, &output); if (rc > 0) { /* wait for the reply by creating a mainloop and running it until * the callbacks are invoked... */ request_id = rc; the_cib->cmds->register_callback(the_cib, request_id, message_timeout_ms, FALSE, NULL, "cibadmin_op_callback", cibadmin_op_callback); mainloop = g_main_loop_new(NULL, FALSE); crm_trace("%s waiting for reply from the local CIB", crm_system_name); crm_info("Starting mainloop"); g_main_loop_run(mainloop); } else if ((rc == -pcmk_err_schema_unchanged) && pcmk__str_eq(cib_action, CIB_OP_UPGRADE, pcmk__str_none)) { report_schema_unchanged(); } else if (rc < 0) { crm_err("Call failed: %s", pcmk_strerror(rc)); fprintf(stderr, "Call failed: %s\n", pcmk_strerror(rc)); if (rc == -pcmk_err_schema_validation) { if (pcmk__str_eq(cib_action, CIB_OP_UPGRADE, pcmk__str_none)) { xmlNode *obj = NULL; int version = 0, rc = 0; rc = the_cib->cmds->query(the_cib, NULL, &obj, command_options); if (rc == pcmk_ok) { update_validation(&obj, &version, 0, TRUE, FALSE); } } else if (output) { validate_xml_verbose(output); } } exit_code = crm_errno2exit(rc); } -#if ENABLE_ACL if (output != NULL && acl_eval_how != acl_eval_unused) { xmlDoc *acl_evaled_doc; - rc = pcmk_acl_evaled_as_namespaces(PCMK_ACL_CRED_USER, acl_cred, - output->doc, &acl_evaled_doc); - if (rc > 0) { + rc = pcmk__acl_annotate_permissions(acl_cred, output->doc, &acl_evaled_doc); + if (rc == pcmk_rc_ok) { free_xml(output); if (acl_eval_how != acl_eval_ns_full) { xmlChar *rendered = NULL; - int rendered_len = 0; - enum pcmk__acl_render_how how = (acl_eval_how == acl_eval_ns_simple) - ? pcmk__acl_render_ns_simple - : (acl_eval_how == acl_eval_text) - ? pcmk__acl_render_text - : (acl_eval_how == acl_eval_color) - ? pcmk__acl_render_color - : /*acl_eval_auto*/ isatty(STDOUT_FILENO) - ? pcmk__acl_render_color - : pcmk__acl_render_text; + enum pcmk__acl_render_how how; + switch(acl_eval_how) { + case acl_eval_ns_simple: + how = pcmk__acl_render_ns_simple; + break; + case acl_eval_text: + how = pcmk__acl_render_text; + break; + case acl_eval_color: + how = pcmk__acl_render_color; + break; + default: + if (/*acl_eval_auto*/ isatty(STDOUT_FILENO)) { + how = pcmk__acl_render_color; + } else { + how = pcmk__acl_render_text; + } + break; + } + if (!pcmk__acl_evaled_render(acl_evaled_doc, how, - &rendered, &rendered_len)) { + &rendered)) { printf("%s\n", (char *) rendered); free(rendered); } else { - local_const_barf("Could not render evaluated access"); + fprintf(stderr, "Could not render evaluated access\n"); crm_exit(CRM_EX_CONFIG); } output = NULL; } else { output = xmlDocGetRootElement(acl_evaled_doc); } - } else if (rc == 0) { - - } else if (rc < 0) { - local_barf("Could not evaluate access per request (%s, rc=%d)\n", - acl_cred, rc); + } else { + fprintf(stderr, "Could not evaluate access per request (%s, error: %s)\n", acl_cred, pcmk_rc_str(rc)); crm_exit(CRM_EX_CONFIG); } } -#endif if (output != NULL) { print_xml_output(output); free_xml(output); } crm_trace("%s exiting normally", crm_system_name); free_xml(input); rc = cib__clean_up_connection(&the_cib); if (exit_code == CRM_EX_OK) { exit_code = pcmk_rc2exitc(rc); } free(host); crm_exit(exit_code); } int do_work(xmlNode * input, int call_options, xmlNode ** output) { /* construct the request */ the_cib->call_timeout = message_timeout_ms; if (strcasecmp(CIB_OP_REPLACE, cib_action) == 0 && pcmk__str_eq(crm_element_name(input), XML_TAG_CIB, pcmk__str_casei)) { xmlNode *status = pcmk_find_cib_element(input, XML_CIB_TAG_STATUS); if (status == NULL) { create_xml_node(input, XML_CIB_TAG_STATUS); } } if (cib_action != NULL) { crm_trace("Passing \"%s\" to variant_op...", cib_action); return cib_internal_op(the_cib, cib_action, host, obj_type, input, output, call_options, cib_user); } else { crm_err("You must specify an operation"); } return -EINVAL; } int do_init(void) { int rc = pcmk_ok; the_cib = cib_new(); rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command); if (rc != pcmk_ok) { crm_err("Could not connect to the CIB: %s", pcmk_strerror(rc)); fprintf(stderr, "Could not connect to the CIB: %s\n", pcmk_strerror(rc)); } return rc; } void cibadmin_op_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data) { exit_code = crm_errno2exit(rc); if (rc == -pcmk_err_schema_unchanged) { report_schema_unchanged(); } else if (rc != pcmk_ok) { crm_warn("Call %s failed (%d): %s", cib_action, rc, pcmk_strerror(rc)); fprintf(stderr, "Call %s failed (%d): %s\n", cib_action, rc, pcmk_strerror(rc)); print_xml_output(output); } else if (pcmk__str_eq(cib_action, CIB_OP_QUERY, pcmk__str_casei) && output == NULL) { crm_err("Query returned no output"); crm_log_xml_err(msg, "no output"); } else if (output == NULL) { crm_info("Call passed"); } else { crm_info("Call passed"); print_xml_output(output); } if (call_id == request_id) { g_main_loop_quit(mainloop); } else { crm_info("Message was not the response we were looking for (%d vs. %d)", call_id, request_id); } } diff --git a/xml/base/access-render-2.xsl b/xml/base/access-render-2.xsl index 3bff3c61f9..6f93ad7ca2 100644 --- a/xml/base/access-render-2.xsl +++ b/xml/base/access-render-2.xsl @@ -1,256 +1,260 @@ \x1b[32m \x1b[34m \x1b[31m \x1b[0m - + + +