From ab1eeac658f3689f08b72e2adf61365a3371e505 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 18 Apr 2024 10:14:11 -0400 Subject: [PATCH 1/9] build: Add a check for a usable libpacemaker. --- configure.ac | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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]) From 108d2cab91dcaa56140604c089b0935431e13b5d Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 18 Apr 2024 10:48:40 -0400 Subject: [PATCH 2/9] pacemaker: Rename pacemaker.h to pcmk.h. Pacemaker already includes a public pacemaker.h which we are going to start using, and gcc doesn't like two header files with the same name in different locations. And rename pacemaker.c to pcmk.c for consistency. --- src/Makefile.am | 4 ++-- src/attr.c | 2 +- src/handler.c | 4 ++-- src/main.c | 2 +- src/{pacemaker.c => pcmk.c} | 2 +- src/{pacemaker.h => pcmk.h} | 0 src/ticket.c | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) rename src/{pacemaker.c => pcmk.c} (99%) rename src/{pacemaker.h => pcmk.h} (100%) 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..3325d8e4 100644 --- a/src/main.c +++ b/src/main.c @@ -66,7 +66,7 @@ #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" diff --git a/src/pacemaker.c b/src/pcmk.c similarity index 99% rename from src/pacemaker.c rename to src/pcmk.c index 9febb087..014e24b3 100644 --- a/src/pacemaker.c +++ b/src/pcmk.c @@ -29,7 +29,7 @@ #include "ticket.h" #include "log.h" #include "attr.h" -#include "pacemaker.h" +#include "pcmk.h" #include "inline-fn.h" 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" From 0647b39c20001e9cb0d9eb253e9a8aa0956e4be7 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 6 May 2024 15:55:55 -0400 Subject: [PATCH 3/9] main: Call pcmk_api_init() if libpacemaker is being used. This is required to initialize various internal pacemaker XML structures and schemas before they are used the first time. Without this, calling into libpacemaker will result in tracebacks. --- src/main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main.c b/src/main.c index 3325d8e4..3d11b5f8 100644 --- a/src/main.c +++ b/src/main.c @@ -61,6 +61,9 @@ #include #include #endif +#ifdef LIBPACEMAKER +#include +#endif #include "log.h" #include "booth.h" #include "config.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); From 4230f7fe364913cbb2793199e0f6119b6047f7f5 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 18 Apr 2024 16:32:02 -0400 Subject: [PATCH 4/9] pacemaker: Pass a bool to pcmk_write_ticket_atomic. --- src/pcmk.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/pcmk.c b/src/pcmk.c index 014e24b3..e1aae0ee 100644 --- a/src/pcmk.c +++ b/src/pcmk.c @@ -17,6 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include #include #include @@ -51,7 +52,7 @@ const char * interpret_rv(int rv) } -static int pcmk_write_ticket_atomic(struct ticket_config *tk, int grant) +static int pcmk_write_ticket_atomic(struct ticket_config *tk, bool grant) { char cmd[COMMAND_MAX]; int rv; @@ -66,9 +67,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, @@ -91,14 +90,14 @@ static int pcmk_write_ticket_atomic(struct ticket_config *tk, int grant) 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); } From 82b117d262781119f1a5d87fc46f68dde76bad0e Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 19 Apr 2024 16:00:35 -0400 Subject: [PATCH 5/9] pacemaker: Use libpacemaker for setting and removing attributes. libpacemaker provides a public API for managing tickets. A first step towards using this API is just the setting and removing code, which is pretty straightforward. We can ignore the XML result portion of the API because in these cases it doesn't contain anything more useful than just the return code. --- src/pcmk.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/pcmk.c b/src/pcmk.c index e1aae0ee..fc67f048 100644 --- a/src/pcmk.c +++ b/src/pcmk.c @@ -33,6 +33,10 @@ #include "pcmk.h" #include "inline-fn.h" +#ifdef LIBPACEMAKER +#include +#include +#endif #define COMMAND_MAX 2048 @@ -101,6 +105,57 @@ static int pcmk_revoke_ticket(struct ticket_config *tk) } +#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; @@ -148,6 +203,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, From fe2ac3b220bf6b8c67489c941a03ddf1e262f813 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 22 Apr 2024 11:24:13 -0400 Subject: [PATCH 6/9] pacemaker: Use libpacemaker for atomic ticket updates. Similar to the previous commit, we can use the new libpacemaker API to also handle atomically modifying a ticket. --- src/pcmk.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/pcmk.c b/src/pcmk.c index fc67f048..58c162ba 100644 --- a/src/pcmk.c +++ b/src/pcmk.c @@ -17,6 +17,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "b_config.h" + #include #include #include @@ -56,6 +58,72 @@ const char * interpret_rv(int rv) } +#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]; @@ -89,6 +157,7 @@ static int pcmk_write_ticket_atomic(struct ticket_config *tk, bool grant) return rv; } +#endif static int pcmk_grant_ticket(struct ticket_config *tk) From 23c1aabb6ffb6c4f1033602772a492bcca7e952b Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 22 Apr 2024 14:14:30 -0400 Subject: [PATCH 7/9] pacemaker: Use libpacemaker to get ticket attributes. While this function isn't currently used, we might as well convert everything to use libpacemaker in case it finds a use in the future. It also provides a good example of how to do this same thing elsewhere in future commits. --- src/pcmk.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/src/pcmk.c b/src/pcmk.c index 58c162ba..22e04a87 100644 --- a/src/pcmk.c +++ b/src/pcmk.c @@ -356,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]; @@ -399,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) From 7a94c72f5c3f1d30fa9d032169f8ef6c76cf4ecf Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 22 Apr 2024 14:45:12 -0400 Subject: [PATCH 8/9] pacemaker: Rename parse_ticket_state to read_ticket_state. This function is now slightly different. Instead of going all the way from reading a file pointer to loading attributes, it now stops before loading the attributes and returns an XML document. The attribute loading happens afterwards in the caller. --- src/pcmk.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/pcmk.c b/src/pcmk.c index 22e04a87..02336b00 100644 --- a/src/pcmk.c +++ b/src/pcmk.c @@ -530,13 +530,12 @@ static int save_attributes(struct booth_config *conf, struct ticket_config *tk, #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 */ @@ -559,8 +558,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", @@ -572,11 +571,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; @@ -584,6 +580,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; @@ -606,7 +603,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 From 0affd58020ffe518e2ba678318f7f803a7a73847 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 22 Apr 2024 15:20:53 -0400 Subject: [PATCH 9/9] pacemaker: Use libpacemaker to load ticket state. The function itself is very simple. The rest of the work is in save_attributes, where it needs to understand both the libpacemaker XML result as well as the result of calling crm_ticket directly. It's still pretty simple, though - just digging around in the XML to set a node pointer correctly. Fixes #136 --- src/pcmk.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/src/pcmk.c b/src/pcmk.c index 02336b00..7f280a0c 100644 --- a/src/pcmk.c +++ b/src/pcmk.c @@ -500,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++) { @@ -528,6 +569,26 @@ 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 read_ticket_state(struct booth_config *conf, struct ticket_config *tk, @@ -634,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 = {