diff --git a/configure.ac b/configure.ac index 97abbbd3..c15b134f 100644 --- a/configure.ac +++ b/configure.ac @@ -360,6 +360,12 @@ AC_DEFINE_UNQUOTED(CRM_DAEMON_USER,"$CRM_DAEMON_USER", User to run Booth daemon BOOTH_PKG_CHECK_VAR([CRM_DAEMON_GROUP], [pacemaker], [daemon_group], [haclient]) AC_DEFINE_UNQUOTED(CRM_DAEMON_GROUP,"$CRM_DAEMON_GROUP", Group to run Booth daemon as) +# If libpacemaker is available, use it +PKG_CHECK_MODULES([LIBPACEMAKER], [libpacemaker >= 2.1.7]) +if test "x$LIBPACEMAKER_CFLAGS" != "xno"; then + AC_DEFINE([LIBPACEMAKER], [1], [use libpacemaker for ticket manipulation]) +fi + # *FLAGS handling goes here ENV_CFLAGS="$CFLAGS" @@ -455,7 +461,7 @@ CFLAGS="$ENV_CFLAGS $OPT_CFLAGS $GDB_FLAGS $OS_CFLAGS \ $COVERAGE_CFLAGS $EXTRA_WARNINGS $WERROR_CFLAGS $LIBGNUTLS_CFLAGS" CPPFLAGS="$ENV_CPPFLAGS $OS_CPPFLAGS $GLIB_CFLAGS $RESMON_CFLAGS $XML_CFLAGS" LDFLAGS="$ENV_LDFLAGS $COVERAGE_LDFLAGS $OS_LDFLAGS" -LIBS="$LIBS $XML_LIBS $LIBGNUTLS_LIBS" +LIBS="$LIBS $XML_LIBS $LIBGNUTLS_LIBS $LIBPACEMAKER_LIBS" # substitute what we need: AC_SUBST([INITDDIR]) diff --git a/src/Makefile.am b/src/Makefile.am index b3c35bbc..712dab57 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,10 +8,10 @@ AM_CPPFLAGS = -I$(top_builddir)/include sbin_PROGRAMS = boothd boothd_SOURCES = config.c main.c raft.c ticket.c transport.c \ - pacemaker.c handler.c request.c attr.c manual.c + pcmk.c handler.c request.c attr.c manual.c noinst_HEADERS = \ - attr.h booth.h handler.h log.h pacemaker.h request.h timer.h \ + attr.h booth.h handler.h log.h pcmk.h request.h timer.h \ auth.h config.h inline-fn.h manual.h raft.h ticket.h transport.h if BUILD_TIMER_C diff --git a/src/attr.c b/src/attr.c index 34df335d..8b01cf94 100644 --- a/src/attr.c +++ b/src/attr.c @@ -21,7 +21,7 @@ #include "attr.h" #include "booth.h" #include "ticket.h" -#include "pacemaker.h" +#include "pcmk.h" void print_geostore_usage(void) { diff --git a/src/handler.c b/src/handler.c index 727b89cb..13b04c8f 100644 --- a/src/handler.c +++ b/src/handler.c @@ -31,7 +31,7 @@ #include "config.h" #include "inline-fn.h" #include "log.h" -#include "pacemaker.h" +#include "pcmk.h" #include "booth.h" #include "handler.h" @@ -131,7 +131,7 @@ void wait_child(int sig) struct ticket_config *tk; /* use waitpid(2) and not wait(2) in order not to interfere - * with popen(2)/pclose(2) and system(2) used in pacemaker.c + * with popen(2)/pclose(2) and system(2) used in pcmk.c */ _FOREACH_TICKET(i, tk) { if (tk_test.path && tk_test.pid > 0 && diff --git a/src/main.c b/src/main.c index 7d932969..3d11b5f8 100644 --- a/src/main.c +++ b/src/main.c @@ -61,12 +61,15 @@ #include #include #endif +#ifdef LIBPACEMAKER +#include +#endif #include "log.h" #include "booth.h" #include "config.h" #include "transport.h" #include "inline-fn.h" -#include "pacemaker.h" +#include "pcmk.h" #include "ticket.h" #include "request.h" #include "attr.h" @@ -1633,6 +1636,10 @@ int main(int argc, char *argv[], char *envp[]) enum qb_log_target_slot i; #endif +#ifdef LIBPACEMAKER + pcmk_api_init(); +#endif + init_set_proc_title(argc, argv, envp); get_time(&start_time); diff --git a/src/pacemaker.c b/src/pcmk.c similarity index 55% rename from src/pacemaker.c rename to src/pcmk.c index 9febb087..7f280a0c 100644 --- a/src/pacemaker.c +++ b/src/pcmk.c @@ -17,6 +17,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "b_config.h" + +#include #include #include #include @@ -29,9 +32,13 @@ #include "ticket.h" #include "log.h" #include "attr.h" -#include "pacemaker.h" +#include "pcmk.h" #include "inline-fn.h" +#ifdef LIBPACEMAKER +#include +#include +#endif #define COMMAND_MAX 2048 @@ -51,7 +58,73 @@ const char * interpret_rv(int rv) } -static int pcmk_write_ticket_atomic(struct ticket_config *tk, int grant) +#ifdef LIBPACEMAKER +static int pcmk_write_ticket_atomic(struct ticket_config *tk, bool grant) +{ + char *owner_s = NULL; + char *expires_s = NULL; + char *term_s = NULL; + char *grant_s = NULL; + char *booth_cfg_name = NULL; + GHashTable *attrs = NULL; + xmlNode *xml = NULL; + int rv; + + attrs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free); + if (attrs == NULL) { + log_error("out of memory"); + return -1; + } + + if (grant) { + grant_s = strdup("true"); + } else { + grant_s = strdup("false"); + } + + if (grant_s == NULL) { + log_error("out of memory"); + return -1; + } + + booth_cfg_name = strdup(booth_conf->name); + + if (booth_cfg_name == NULL) { + log_error("out of memory"); + free(grant_s); + return -1; + } + + if (asprintf(&owner_s, "%" PRIi32, get_node_id(tk->leader)) == -1 || + asprintf(&expires_s, "%" PRIi64, wall_ts(&tk->term_expires)) == -1 || + asprintf(&term_s, "%" PRIi64, (int64_t) tk->current_term) == -1) { + log_error("out of memory"); + free(owner_s); + free(expires_s); + free(term_s); + free(grant_s); + return -1; + } + + g_hash_table_insert(attrs, (gpointer) "owner", owner_s); + g_hash_table_insert(attrs, (gpointer) "expires", expires_s); + g_hash_table_insert(attrs, (gpointer) "term", term_s); + g_hash_table_insert(attrs, (gpointer) "granted", grant_s); + g_hash_table_insert(attrs, (gpointer) "booth-cfg-name", booth_conf); + + rv = pcmk_ticket_set_attr(&xml, tk->name, attrs, true); + g_hash_table_remove_all(attrs); + xmlFreeNode(xml); + + if (rv != pcmk_rc_ok) { + log_error("pcmk_write_ticket_atomic: %s", pcmk_rc_str(rv)); + return -1; + } + + return 0; +} +#else +static int pcmk_write_ticket_atomic(struct ticket_config *tk, bool grant) { char cmd[COMMAND_MAX]; int rv; @@ -66,9 +139,7 @@ static int pcmk_write_ticket_atomic(struct ticket_config *tk, int grant) "-S term --attr-value=%" PRIi64 " " "-S booth-cfg-name --attr-value=%s", tk->name, - (grant > 0 ? "-g" : - grant < 0 ? "-r" : - ""), + grant ? "-g" : "-r", (int32_t)get_node_id(tk->leader), (int64_t)wall_ts(&tk->term_expires), (int64_t)tk->current_term, @@ -86,22 +157,74 @@ static int pcmk_write_ticket_atomic(struct ticket_config *tk, int grant) return rv; } +#endif static int pcmk_grant_ticket(struct ticket_config *tk) { - return pcmk_write_ticket_atomic(tk, +1); + return pcmk_write_ticket_atomic(tk, true); } static int pcmk_revoke_ticket(struct ticket_config *tk) { - return pcmk_write_ticket_atomic(tk, -1); + return pcmk_write_ticket_atomic(tk, false); } +#ifdef LIBPACEMAKER +static int pcmk_set_attr(struct ticket_config *tk, const char *attr, const char *val) +{ + GHashTable *attrs = NULL; + xmlNode *xml = NULL; + int rv; + + attrs = g_hash_table_new(g_str_hash, g_str_equal); + if (attrs == NULL) { + log_error("out of memory"); + return -1; + } + + g_hash_table_insert(attrs, (gpointer) attr, (gpointer) val); + + rv = pcmk_ticket_set_attr(&xml, tk->name, attrs, false); + g_hash_table_destroy(attrs); + xmlFreeNode(xml); + + if (rv != pcmk_rc_ok) { + log_error("pcmk_set_attr: %s", pcmk_rc_str(rv)); + return -1; + } + + return 0; +} + +static int pcmk_del_attr(struct ticket_config *tk, const char *attr) +{ + GList *attrs = NULL; + xmlNode *xml = NULL; + int rv; + + attrs = g_list_append(attrs, (gpointer) attr); + if (attrs == NULL) { + log_error("out of memory"); + return -1; + } + + rv = pcmk_ticket_remove_attr(&xml, tk->name, attrs, false); + g_list_free(attrs); + xmlFreeNode(xml); + + if (rv != pcmk_rc_ok) { + log_error("pcmk_del_attr: %s", pcmk_rc_str(rv)); + return -1; + } + + return 0; +} +#else static int _run_crm_ticket(char *cmd) { int i, rv; @@ -149,6 +272,7 @@ static int pcmk_del_attr(struct ticket_config *tk, const char *attr) return _run_crm_ticket(cmd); } +#endif typedef int (*attr_f)(struct booth_config *conf, struct ticket_config *tk, @@ -232,6 +356,91 @@ struct attr_tab attr_handlers[] = { /* get_attr is currently not used and has not been tested */ +#ifdef LIBPACEMAKER +static int attr_from_xml(xmlNode *xml, const char *ticket_id, const char *attr, + char **vp) +{ + xmlXPathObject *xpath_obj = NULL; + xmlXPathContextPtr xpath_ctx = NULL; + xmlNode *match = NULL; + xmlAttr *attr_xml = NULL; + char *expr = NULL; + int rv = 0; + + rv = asprintf(&expr, "//" PCMK_XE_PACEMAKER_RESULT "/" PCMK_XE_TICKETS "/" + PCMK_XE_TICKET "[@" PCMK_XA_ID "=\"%s\"]/" PCMK_XE_ATTRIBUTE + "[@" PCMK_XA_NAME "=\"%s\"]", ticket_id, attr); + if (rv != 0) { + log_error("attr_from_xml: out of memory"); + goto done; + } + + xpath_ctx = xmlXPathNewContext(xml->doc); + if (xpath_ctx == NULL) { + log_error("attr_from_xml: could not create xpath context"); + rv = -1; + goto done; + } + + xpath_obj = xmlXPathEvalExpression((const xmlChar *) expr, xpath_ctx); + if (xpath_obj == NULL || xpath_obj->nodesetval == NULL || xpath_obj->nodesetval->nodeNr) { + log_error("attr_from_xml: could not evaluate xpath expression"); + rv = -1; + goto done; + } + + match = xpath_obj->nodesetval->nodeTab[0]; + if (match->type != XML_ELEMENT_NODE) { + log_error("attr_from_xml: xpath result is not an XML_ELEMENT_NODE"); + rv = -1; + goto done; + } + + attr_xml = xmlHasProp(match, (const xmlChar *) attr); + if (attr_xml != NULL) { + *vp = g_strdup((const char *) attr_xml->children->content); + } + +done: + if (expr != NULL) { + free(expr); + } + + if (xpath_obj != NULL) { + xmlXPathFreeObject(xpath_obj); + } + + if (xpath_ctx != NULL) { + xmlXPathFreeContext(xpath_ctx); + } + + if (attr_xml != NULL) { + xmlFreeProp(attr_xml); + } + + return rv; +} + + +static int pcmk_get_attr(struct ticket_config *tk, const char *attr, const char **vp) +{ + xmlNode *xml = NULL; + int rv; + + rv = pcmk_ticket_get_attr(&xml, tk->name, attr, NULL); + + if (rv != pcmk_rc_ok) { + log_error("pcmk_get_attr: %s", pcmk_rc_str(rv)); + xmlFreeNode(xml); + return -1; + } + + rv = attr_from_xml(xml, tk->name, attr, (char **) vp); + + xmlFreeNode(xml); + return rv; +} +#else static int pcmk_get_attr(struct ticket_config *tk, const char *attr, const char **vp) { char cmd[COMMAND_MAX]; @@ -275,6 +484,7 @@ static int pcmk_get_attr(struct ticket_config *tk, const char *attr, const char } return rv | pipe_rv; } +#endif static int save_attributes(struct booth_config *conf, struct ticket_config *tk, xmlDocPtr doc) @@ -290,10 +500,51 @@ static int save_attributes(struct booth_config *conf, struct ticket_config *tk, tk_log_error("crm_ticket xml output empty"); return -EINVAL; } - if (xmlStrcmp(n->name, (const xmlChar *)"ticket_state")) { + + if (xmlStrcmp(n->name, (const xmlChar *) PCMK_XE_PACEMAKER_RESULT) == 0) { + xmlNode *tickets_node = NULL; + xmlNode *ticket_node = NULL; + + /* This is XML from a libpacemaker API call. Move the node pointer to + * the ticket element containing the attributes we want to copy. + */ + + /* Look for a child node named . */ + for (xmlNode *child = n->children; child != NULL; child = child->next) { + if (xmlStrcmp(child->name, (const xmlChar *) PCMK_XE_TICKETS) == 0) { + tickets_node = child; + break; + } + } + + if (tickets_node == NULL) { + tk_log_error("API result does not match expected"); + return -EINVAL; + } + + /* Under that should be a single node containing the attributes + * we want to copy. libpacemaker should only return one node because we + * asked for a specific ticket, but just to be safe... + */ + for (xmlNode *child = tickets_node->children; child != NULL; child = child->next) { + if (xmlStrcmp(child->name, (const xmlChar *) PCMK_XE_TICKET) == 0) { + ticket_node = child; + break; + } + } + + if (ticket_node == NULL) { + tk_log_error("API result does not match expected"); + return -EINVAL; + } + + n = ticket_node; + } else if (xmlStrcmp(n->name, (const xmlChar *) "ticket_state") != 0) { + /* This isn't any XML we expect */ tk_log_error("crm_ticket xml root element not ticket_state"); return -EINVAL; - } + } + for (attr = n->properties; attr; attr = attr->next) { v = xmlGetProp(n, attr->name); for (atp = attr_handlers; atp->name; atp++) { @@ -318,15 +569,34 @@ static int save_attributes(struct booth_config *conf, struct ticket_config *tk, } +#ifdef LIBPACEMAKER +static int pcmk_load_ticket(struct ticket_config *tk) +{ + xmlNode *xml = NULL; + int rv; + + rv = pcmk_ticket_state(&xml, tk->name); + + if (rv == pcmk_rc_ok) { + rv = save_attributes(tk, xml->doc); + } else { + log_error("pcmk_load_ticket: %s", pcmk_rc_str(rv)); + rv = -1; + } + + xmlFreeNode(xml); + return rv; +} +#else + #define CHUNK_SIZE 256 -static int parse_ticket_state(struct booth_config *conf, struct ticket_config *tk, - FILE *p) +static int read_ticket_state(struct booth_config *conf, struct ticket_config *tk, + xmlDocPtr *doc, FILE *p) { int rv = 0; GString *input = NULL; char line[CHUNK_SIZE]; - xmlDocPtr doc = NULL; int opts = XML_PARSE_COMPACT | XML_PARSE_NONET; /* skip first two lines of output */ @@ -349,8 +619,8 @@ static int parse_ticket_state(struct booth_config *conf, struct ticket_config *t } } - doc = xmlReadDoc((const xmlChar *) input->str, NULL, NULL, opts); - if (doc == NULL) { + *doc = xmlReadDoc((const xmlChar *) input->str, NULL, NULL, opts); + if (*doc == NULL) { const xmlError *errptr = xmlGetLastError(); if (errptr) { tk_log_error("crm_ticket xml parse failed (domain=%d, level=%d, code=%d): %s", @@ -362,11 +632,8 @@ static int parse_ticket_state(struct booth_config *conf, struct ticket_config *t rv = -EINVAL; goto out; } - rv = save_attributes(conf, tk, doc); out: - if (doc) - xmlFreeDoc(doc); if (input) g_string_free(input, TRUE); return rv; @@ -374,6 +641,7 @@ static int parse_ticket_state(struct booth_config *conf, struct ticket_config *t static int pcmk_load_ticket(struct booth_config *conf, struct ticket_config *tk) { + xmlDocPtr doc; char cmd[COMMAND_MAX]; int rv = 0, pipe_rv; int res; @@ -396,7 +664,11 @@ static int pcmk_load_ticket(struct booth_config *conf, struct ticket_config *tk) return (pipe_rv != 0 ? pipe_rv : EINVAL); } - rv = parse_ticket_state(conf, tk, p); + rv = read_ticket_state(conf, tk, &doc, p); + if (rv == 0) { + rv = save_attributes(conf, tk, doc); + xmlFreeDoc(doc); + } if (!tk->leader) { /* Hmm, no site found for the ticket we have in the @@ -423,6 +695,7 @@ static int pcmk_load_ticket(struct booth_config *conf, struct ticket_config *tk) } return rv | pipe_rv; } +#endif struct ticket_handler pcmk_handler = { diff --git a/src/pacemaker.h b/src/pcmk.h similarity index 100% rename from src/pacemaker.h rename to src/pcmk.h diff --git a/src/ticket.c b/src/ticket.c index 53e5288c..cc9c2954 100644 --- a/src/ticket.c +++ b/src/ticket.c @@ -34,7 +34,7 @@ #endif #include "ticket.h" #include "config.h" -#include "pacemaker.h" +#include "pcmk.h" #include "inline-fn.h" #include "log.h" #include "booth.h"