diff options
author | Jan Pokorný <jpokorny@redhat.com> | 2014-04-25 21:35:17 +0200 |
---|---|---|
committer | Jan Pokorný <jpokorny@redhat.com> | 2014-04-25 21:56:41 +0200 |
commit | 6d68205c666e84995f0b9c1d4ad17f7fb22cd27d (patch) | |
tree | d286cf8c5ccf7bd6ee53d30b50a324a1eaebf748 /__root__/ccs-flatten | |
parent | aa08c2c7106dca9423fd3057ecd8ceb111083928 (diff) | |
download | clufter-6d68205c666e84995f0b9c1d4ad17f7fb22cd27d.tar.gz clufter-6d68205c666e84995f0b9c1d4ad17f7fb22cd27d.tar.xz clufter-6d68205c666e84995f0b9c1d4ad17f7fb22cd27d.zip |
Embed ccs_flatten utility and make (almost) directly usable
(prerequisite is the previous commit)
Signed-off-by: Jan Pokorný <jpokorny@redhat.com>
Diffstat (limited to '__root__/ccs-flatten')
-rw-r--r-- | __root__/ccs-flatten/Makefile | 13 | ||||
-rw-r--r-- | __root__/ccs-flatten/flatten.c | 240 | ||||
-rw-r--r-- | __root__/ccs-flatten/list.h | 91 | ||||
-rw-r--r-- | __root__/ccs-flatten/resgroup.h | 122 | ||||
-rw-r--r-- | __root__/ccs-flatten/reslist.c | 518 | ||||
-rw-r--r-- | __root__/ccs-flatten/reslist.h | 205 | ||||
-rw-r--r-- | __root__/ccs-flatten/resrules.c | 971 | ||||
-rw-r--r-- | __root__/ccs-flatten/restree.c | 723 | ||||
-rw-r--r-- | __root__/ccs-flatten/xmlconf.c | 139 | ||||
-rw-r--r-- | __root__/ccs-flatten/xmlconf.h | 11 |
10 files changed, 3033 insertions, 0 deletions
diff --git a/__root__/ccs-flatten/Makefile b/__root__/ccs-flatten/Makefile new file mode 100644 index 0000000..8172cb9 --- /dev/null +++ b/__root__/ccs-flatten/Makefile @@ -0,0 +1,13 @@ +.PHONY: all clean + +TARGET = ccs_flatten +OBJS = flatten.o reslist.o resrules.o restree.o xmlconf.o +CFLAGS += $(shell pkg-config --cflags libxml-2.0) -I. +LDFLAGS += $(shell pkg-config --libs libxml-2.0) + +all: ${TARGET} +${TARGET}: ${OBJS} + ${CC} ${CFLAGS} ${LDFLAGS} $^ -o $@ + +clean: + ${RM} ${TARGET} ${OBJS} diff --git a/__root__/ccs-flatten/flatten.c b/__root__/ccs-flatten/flatten.c new file mode 100644 index 0000000..068056b --- /dev/null +++ b/__root__/ccs-flatten/flatten.c @@ -0,0 +1,240 @@ +/* + Copyright Red Hat, Inc. 2004-2006 + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ +#include <libxml/parser.h> +#include <libxml/xmlmemory.h> +#include <libxml/xpath.h> +#include <stdlib.h> +#include <stdio.h> +#include <resgroup.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <list.h> +#include <reslist.h> +#include <xmlconf.h> +#include <libgen.h> + +#define shift() {++argv; --argc;} + +const char *agentpath = RESOURCE_ROOTDIR; + +static xmlNode * +get_rm_node(xmlDocPtr doc) +{ + xmlNodePtr n, o; + + if (!doc) + return NULL; + n = xmlDocGetRootElement(doc); + if (!n) + return NULL; + if (strcmp((char *)n->name, "cluster")) { + fprintf(stderr, "Expected cluster tag, got %s\n", (char *)n->name); + return NULL; + } + + for (o = n->xmlChildrenNode; o; o = o->next) { + if (o->type != XML_ELEMENT_NODE) + continue; + if (!strcmp((char *)o->name, "rm")) + return o; + } + + return NULL; +} + +static void +remove_resources_block(xmlNodePtr rm) +{ + xmlNodePtr o, r = NULL; + + for (o = rm->xmlChildrenNode; o; o = o->next) { + if (o->type != XML_ELEMENT_NODE) + continue; + if (!strcmp((char *)o->name, "resources")) { + r = o; + break; + } + } + + if (!r) + return; + + xmlUnlinkNode(r); + xmlFreeNode(r); +} + +static int +replace_resource(xmlNodePtr rm, char *restype, char *primattr, char *ident, xmlNodePtr n) +{ + xmlNodePtr o, r = NULL; + char *p; + + for (o = rm->xmlChildrenNode; o; o = o->next) { + if (o->type != XML_ELEMENT_NODE) + continue; + if (!strcmp((char *)o->name, restype)) { + p = (char *)xmlGetProp(o, (xmlChar *) primattr); + if (!strcmp(p, ident)) { + r = o; + break; + } + } + } + + if (!r) + return -1; + + xmlUnlinkNode(r); + xmlFreeNode(r); + + xmlAddChild(rm, n); + + return 0; +} + +static int +flatten(int argc, char **argv, xmlDocPtr * doc) +{ + xmlDocPtr d = NULL; + xmlNode *n = NULL, *rm = NULL, *new_rb = NULL; + resource_rule_t *rulelist = NULL; + resource_t *reslist = NULL, *curres; + resource_node_t *tree = NULL, *rn; + FILE *f = stdout; + int ret = 0; + + conf_setconfig(argv[0]); + if (conf_open() < 0) { + xmlFree(new_rb); + goto out; + } + + while (argc >= 2) { + shift(); + if (!strcmp(argv[0], "-r")) { + if (!new_rb) + new_rb = xmlNewNode(NULL, (xmlChar *) "rm"); + } else { + if (f == stdout) + f = fopen(argv[0], "w+"); + } + } + + d = conf_get_doc(); + rm = get_rm_node(d); + + load_resource_rules(agentpath, &rulelist); + if (!rulelist) { + fprintf(stderr, "No resource rules available\n"); + goto out; + } + load_resources(&reslist, &rulelist); + build_resource_tree(&tree, &rulelist, &reslist); + if (!tree) { + fprintf(stderr, "No resource trees defined; nothing to do\n"); + goto out; + } +#ifdef DEBUG + fprintf(stderr, "Resources %p tree %p\n", reslist, tree); +#endif + + shift(); + + list_do(&tree, rn) { + n = NULL; + + curres = rn->rn_resource; + +#ifdef DEBUG + fprintf(stderr, "Flatten %s:%s ... \n", curres->r_rule->rr_type, + curres->r_attrs[0].ra_value); +#endif + if (res_flatten(&n, new_rb, &tree, curres)) { + fprintf(stderr, "FAIL 1\n"); + ret = -1; + goto out; + } + + if (replace_resource(rm, curres->r_rule->rr_type, + curres->r_attrs[0].ra_name, curres->r_attrs[0].ra_value, n) != 0) { + fprintf(stderr, "FAIL 2\n"); + ret = -1; + goto out; + } + + } + while (!list_done(&tree, rn)) ; + + remove_resources_block(rm); + if (new_rb) { + xmlAddChild(rm, new_rb); + } + + xmlDocFormatDump(f, d, 1); + if (f != stdout) + fclose(f); + + out: + if (ret < 0) { + xmlFreeDoc(d); + } else { + *doc = d; + } + conf_close(); + destroy_resource_tree(&tree); + destroy_resources(&reslist); + destroy_resource_rules(&rulelist); + + return ret; +} + +static void +usage(const char *arg0, int ret) +{ + fprintf(stderr, "usage: %s <input.conf> [output.conf] [-r]\n", arg0); + exit(ret); +} + +int +main(int argc, char **argv) +{ + char *arg0 = basename(argv[0]); + int ret = 0; + xmlDocPtr doc = NULL; + + if (argc < 2) { + usage(arg0, 1); + } + + if (!strcasecmp(argv[1], "-h") || !strcasecmp(argv[1], "-?")) { + usage(arg0, 0); + } + + xmlInitParser(); + xmlIndentTreeOutput = 1; + xmlKeepBlanksDefault(0); + + shift(); + ret = flatten(argc, argv, &doc); + + //if (doc) + //xmlFreeDoc(doc); + xmlCleanupParser(); + return ret; +} diff --git a/__root__/ccs-flatten/list.h b/__root__/ccs-flatten/list.h new file mode 100644 index 0000000..d43ba01 --- /dev/null +++ b/__root__/ccs-flatten/list.h @@ -0,0 +1,91 @@ +#ifndef _LIST_H +# define _LIST_H + +/** + Simple list handlig macros. + Needs rewrite or inclusion of /usr/include/linux/list.h as a replacement. + */ + +/* Must be first if structure is going to use it. */ +struct list_entry { + struct list_entry *le_next, *le_prev; +}; + +# define list_head() struct list_entry _list_head + +# define le(p) (&((*p)._list_head)) + +# define list_insert(list, newnode) \ +do { \ + if (!(*list)) { \ + le(newnode)->le_next = \ + le(newnode)->le_prev = le(newnode); \ + *list = (void *)le(newnode); \ + } else { \ + le(*list)->le_prev->le_next = le(newnode); \ + le(newnode)->le_next = le(*list); \ + le(newnode)->le_prev = le(*list)->le_prev; \ + le(*list)->le_prev = le(newnode); \ + } \ +} while (0) + +# define list_prepend(list, newnode) \ +do { \ + list_insert(list, newnode); \ + *list = newnode; \ +} while (0) + +# define list_remove(list, oldnode) \ +do { \ + if (le(oldnode) == le(*list)) { \ + *list = (void *)le(*list)->le_next; \ + } \ + if (le(oldnode) == le(*list)) { \ + le(oldnode)->le_next = NULL; \ + le(oldnode)->le_prev = NULL; \ + *list = NULL; \ + } else { \ + le(oldnode)->le_next->le_prev = le(oldnode)->le_prev; \ + le(oldnode)->le_prev->le_next = le(oldnode)->le_next; \ + le(oldnode)->le_prev = NULL; \ + le(oldnode)->le_next = NULL; \ + } \ +} while (0) + +/* +list_do(list, node) { + stuff; +} while (!list_done(list, node)); + */ +# define list_do(list, curr) \ + if (*list && (curr = *list)) do + +# define list_done(list, curr) \ + (curr && (((curr = (void *)le(curr)->le_next)) && (curr == *list))) + +/* + * list_for(list, tmp, counter) { + * stuff; + * } + * + * counter = # of items in list when done. + * * sets cnt to 0 before even checking list; + * * checks for valid list + * * traverses list, incrementing counter. If we get to the for loop, + * there must be at least one item in the list + */ +# define list_for(list, curr, cnt) \ + if (!(cnt=0) && (list != NULL) && (*list != NULL)) \ + for (curr = *list; \ + (cnt == 0) || (curr != *list); \ + curr = (void*)le(curr)->le_next, \ + cnt++) + +# define list_for_rev(list, curr, cnt) \ + if (!(cnt=0) && list && *list) \ + for (curr = (void *)(le(*list)->le_prev); \ + (cnt == 0) || ((void *)curr != le(*list)->le_prev); \ + curr = (void*)(le(curr)->le_prev), \ + cnt++) + +#endif diff --git a/__root__/ccs-flatten/resgroup.h b/__root__/ccs-flatten/resgroup.h new file mode 100644 index 0000000..50db8e0 --- /dev/null +++ b/__root__/ccs-flatten/resgroup.h @@ -0,0 +1,122 @@ +#ifndef __RESGROUP_H +# define __RESGROUP_H + +# include <pthread.h> +# include <stdio.h> +# include <stdint.h> +# include <stdlib.h> +# include <string.h> +# include <unistd.h> +# include <sys/types.h> +# include <errno.h> + +/* Requests */ +# define RG_SUCCESS 0 +# define RG_FAIL 1 +# define RG_START 2 +# define RG_STOP 3 +# define RG_STATUS 4 +# define RG_DISABLE 5 +# define RG_STOP_RECOVER 6 +# define RG_START_RECOVER 7 +# define RG_RESTART 8 +# define RG_EXITING 9 +# define RG_INIT 10 +# define RG_ENABLE 11 +# define RG_STATUS_NODE 12 +# define RG_RELOCATE 13 +# define RG_CONDSTOP 14 +# define RG_CONDSTART 15 +# define RG_START_REMOTE 16 /* Part of a relocate */ +# define RG_STOP_USER 17 /* User-stop request */ +# define RG_STOP_EXITING 18 + /* Exiting. */ +# define RG_LOCK 19 +# define RG_UNLOCK 20 +# define RG_QUERY_LOCK 21 +# define RG_MIGRATE 22 +# define RG_FREEZE 23 +# define RG_UNFREEZE 24 +# define RG_STATUS_INQUIRY 25 +# define RG_CONVALESCE 26 +# define RG_NONE 999 + +/* Resource group states (for now) */ +# define RG_STATE_BASE 110 +# define RG_STATE_STOPPED 110 /** Resource group is stopped */ +# define RG_STATE_STARTING 111 /** Resource is starting */ +# define RG_STATE_STARTED 112 /** Resource is started */ +# define RG_STATE_STOPPING 113 /** Resource is stopping */ +# define RG_STATE_FAILED 114 + /** Resource has failed */ +# define RG_STATE_UNINITIALIZED 115 + /** Thread not running yet */ +# define RG_STATE_CHECK 116 + /** Checking status */ +# define RG_STATE_ERROR 117 + /** Recoverable error */ +# define RG_STATE_RECOVER 118 /** Pending recovery */ +# define RG_STATE_DISABLED 119 /** Resource not allowd to run */ +# define RG_STATE_MIGRATE 120 /** Resource migrating */ + +# define DEFAULT_CHECK_INTERVAL 10 + +/* Resource group flags (for now) */ +# define RG_FLAG_FROZEN (1<<0) + /** Resource frozen */ +# define RG_FLAG_PARTIAL (1<<1) + /** One or more non-critical + resources offline */ + +/* Return codes */ +# define RG_EEXCL -16 /* Service not runnable due to + the fact that it is tagged + exclusive and there are no + empty nodes. */ +# define RG_EDOMAIN -15 /* Service not runnable given the + set of nodes and its failover + domain */ +# define RG_ESCRIPT -14 /* S/Lang script failed */ +# define RG_EFENCE -13 /* Fencing operation pending */ +# define RG_ENODE -12 /* Node is dead/nonexistent */ +# define RG_EFROZEN -11 /* Forward compat. with -HEAD */ +# define RG_ERUN -10 + /* Service is already running */ +# define RG_EQUORUM -9 /* Operation requires quorum */ +# define RG_EINVAL -8 /* Invalid operation for resource */ +# define RG_EDEPEND -7 /* Operation violates dependency */ +# define RG_EAGAIN -6 /* Try again */ +# define RG_EDEADLCK -5 /* Aborted - would deadlock */ +# define RG_ENOSERVICE -4 /* Service does not exist */ +# define RG_EFORWARD -3 /* Service not mastered locally */ +# define RG_EABORT -2 /* Abort; service unrecoverable */ +# define RG_EFAIL -1 /* Generic failure */ +# define RG_ESUCCESS 0 +# define RG_YES 1 +# define RG_NO 2 + +const char *rg_strerror(int val); + +/* + * Fail-over domain states + */ +# define FOD_ILLEGAL 0 +# define FOD_GOOD 1 +# define FOD_BETTER 2 +# define FOD_BEST 3 + +/* + Fail-over domain flags + */ +# define FOD_ORDERED (1<<0) +# define FOD_RESTRICTED (1<<1) +# define FOD_NOFAILBACK (1<<2) + +/* + Status tree flags + */ +# define SFL_FAILURE (1<<0) +# define SFL_RECOVERABLE (1<<1) +# define SFL_PARTIAL (1<<2) + +#endif diff --git a/__root__/ccs-flatten/reslist.c b/__root__/ccs-flatten/reslist.c new file mode 100644 index 0000000..4c50c90 --- /dev/null +++ b/__root__/ccs-flatten/reslist.c @@ -0,0 +1,518 @@ +/* + Copyright Red Hat, Inc. 2004 + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ +#include <libxml/parser.h> +#include <libxml/xmlmemory.h> +#include <libxml/xpath.h> +#include <stdlib.h> +#include <stdio.h> +#include <resgroup.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <list.h> +#include <libgen.h> +#include <reslist.h> +#include <xmlconf.h> + +void +res_build_name(char *buf, size_t buflen, resource_t * res) +{ + snprintf(buf, buflen, "%s:%s", res->r_rule->rr_type, res->r_attrs[0].ra_value); +} + +/** + Find and determine an attribute's value. + + @param res Resource node to look examine + @param attrname Attribute to retrieve. + @return value of attribute or NULL if not found + */ +char * +res_attr_value(resource_t * res, const char *attrname) +{ + resource_attr_t *ra; + int x; + + for (x = 0; res->r_attrs && res->r_attrs[x].ra_name; x++) { + if (strcmp(attrname, res->r_attrs[x].ra_name)) + continue; + + ra = &res->r_attrs[x]; + + if (ra->ra_flags & RA_INHERIT) + /* Can't check inherited resources */ + return NULL; + + return ra->ra_value; + } + + return NULL; +} + +/** + Find and determine an attribute's value. Takes into account inherited + attribute flag, and append attribute flag, which isn't implemented yet. + + @param node Resource tree node to look examine + @param attrname Attribute to retrieve. + @param ptype Resource type to look for (if inheritance) + @return value of attribute or NULL if not found + */ +static char * +_attr_value(resource_node_t * node, const char *attrname, const char *ptype) +{ + resource_t *res; + resource_attr_t *ra; + char *c, p_type[32]; + ssize_t len; + int x; + + if (!node) + return NULL; + + res = node->rn_resource; + + /* Go up the tree if it's not the right parent type */ + if (ptype && strcmp(res->r_rule->rr_type, ptype)) + return _attr_value(node->rn_parent, attrname, ptype); + + for (x = 0; res->r_attrs && res->r_attrs[x].ra_name; x++) { + if (strcmp(attrname, res->r_attrs[x].ra_name)) + continue; + + ra = &res->r_attrs[x]; + + if (!(ra->ra_flags & RA_INHERIT)) + return ra->ra_value; + /* + Handle resource_type%field to be more precise, so we + don't have to worry about this being a child + of an unexpected type. E.g. lots of things have the + "name" attribute. + */ + c = strchr(ra->ra_value, '%'); + if (!c) { + /* Someone doesn't care or uses older + semantics on inheritance */ + return _attr_value(node->rn_parent, ra->ra_value, NULL); + } + + len = (c - ra->ra_value); + memset(p_type, 0, sizeof(p_type)); + memcpy(p_type, ra->ra_value, len); + + /* Skip the "%" and recurse */ + return _attr_value(node->rn_parent, ++c, p_type); + } + + return NULL; +} + +char * +attr_value(resource_node_t * node, const char *attrname) +{ + return _attr_value(node, attrname, NULL); +} + +char * +primary_attr_value(resource_t * res) +{ + int x; + resource_attr_t *ra; + + for (x = 0; res->r_attrs && res->r_attrs[x].ra_name; x++) { + ra = &res->r_attrs[x]; + + if (!(ra->ra_flags & RA_PRIMARY)) + continue; + + return ra->ra_value; + } + + return NULL; +} + +/** + Find a resource given its reference. A reference is the value of the + primary attribute. + + @param reslist List of resources to traverse. + @param type Type of resource to look for. + @param ref Reference + @return Resource matching type/ref or NULL if none. + */ +resource_t * +find_resource_by_ref(resource_t ** reslist, char *type, char *ref) +{ + resource_t *curr; + int x; + + list_do(reslist, curr) { + if (strcmp(curr->r_rule->rr_type, type)) + continue; + + /* + This should be one operation - the primary attr + is generally at the head of the array. + */ + for (x = 0; curr->r_attrs && curr->r_attrs[x].ra_name; x++) { + if (!(curr->r_attrs[x].ra_flags & RA_PRIMARY)) + continue; + if (strcmp(ref, curr->r_attrs[x].ra_value)) + continue; + + return curr; + } + } + while (!list_done(reslist, curr)) ; + + return NULL; +} + +/** + Store a resource in the resource list if it's legal to do so. + Otherwise, don't store it. + Note: This function needs to be rewritten; it's way too long and way + too indented. + + @param reslist Resource list to store the new resource. + @param newres Resource to store + @return 0 on succes; nonzero on failure. + */ +int +store_resource(resource_t ** reslist, resource_t * newres) +{ + resource_t *curr; + int x, y; + + if (!*reslist) { + /* first resource */ + list_insert(reslist, newres); + return 0; + } + + list_do(reslist, curr) { + + if (strcmp(curr->r_rule->rr_type, newres->r_rule->rr_type)) + continue; + + for (x = 0; newres->r_attrs && newres->r_attrs[x].ra_name; x++) { + /* + Look for conflicting primary/unique keys + */ + if (!(newres->r_attrs[x].ra_flags & (RA_PRIMARY | RA_UNIQUE))) + continue; + + for (y = 0; curr->r_attrs[y].ra_name; y++) { + if (curr->r_attrs[y].ra_flags & RA_INHERIT) + continue; + + if (strcmp(curr->r_attrs[y].ra_name, newres->r_attrs[x].ra_name)) + continue; + if (!strcmp(curr->r_attrs[y].ra_value, newres->r_attrs[x].ra_value)) { + /* + Unique/primary is not unique + */ + fprintf(stderr, + "%s attribute collision. " + "type=%s attr=%s value=%s\n", + (newres->r_attrs[x].ra_flags & + RA_PRIMARY) ? "Primary" : + "Unique", + newres->r_rule->rr_type, + newres->r_attrs[x].ra_name, newres->r_attrs[x].ra_value); + return -1; + } + break; + } + } + } + while (!list_done(reslist, curr)) ; + + list_insert(reslist, newres); + return 0; +} + +/** + Obliterate a resource_t structure. + + @param res Resource to free. + */ +void +destroy_resource(resource_t * res) +{ + int x; + + if (res->r_name) + free(res->r_name); + + if (res->r_attrs) { + for (x = 0; res->r_attrs && res->r_attrs[x].ra_name; x++) { + free(res->r_attrs[x].ra_name); + free(res->r_attrs[x].ra_value); + } + + free(res->r_attrs); + } + + if (res->r_actions) { + /* Don't free the strings; they're part of the rule */ + free(res->r_actions); + } + + free(res); +} + +/** + Obliterate a resource_t list. + + @param list Resource list to free. + */ +void +destroy_resources(resource_t ** list) +{ + resource_t *res; + + while ((res = *list)) { + list_remove(list, res); + destroy_resource(res); + } +} + +void * +act_dup(resource_act_t * acts) +{ + int x; + resource_act_t *newacts; + + for (x = 0; acts[x].ra_name; x++) ; + + ++x; + x *= sizeof(resource_act_t); + + newacts = malloc(x); + if (!newacts) + return NULL; + + memcpy(newacts, acts, x); + + return newacts; +} + +/* Copied from resrules.c -- _get_actions */ +static void +_get_actions_ccs(const char *base, resource_t * res) +{ + char xpath[256]; + int idx = 0; + char *act, *ret; + int interval, timeout, depth; + + do { + /* setting these to -1 prevents overwriting with 0 */ + interval = -1; + depth = -1; + act = NULL; + timeout = -1; + + snprintf(xpath, sizeof(xpath), "%s/action[%d]/@name", base, ++idx); + + if (conf_get(xpath, &act) != 0) + break; + + snprintf(xpath, sizeof(xpath), "%s/action[%d]/@timeout", base, idx); + if (conf_get(xpath, &ret) == 0 && ret) { + timeout = expand_time(ret); + if (timeout < 0) + timeout = 0; + free(ret); + } + + snprintf(xpath, sizeof(xpath), "%s/action[%d]/@interval", base, idx); + if (conf_get(xpath, &ret) == 0 && ret) { + interval = expand_time(ret); + if (interval < 0) + interval = 0; + free(ret); + } + + if (!strcmp(act, "status") || !strcmp(act, "monitor")) { + snprintf(xpath, sizeof(xpath), "%s/action[%d]/@depth", base, idx); + if (conf_get(xpath, &ret) == 0 && ret) { + depth = atoi(ret); + if (depth < 0) + depth = 0; + + /* */ + if (ret[0] == '*') + depth = -1; + free(ret); + } + } + + if (store_action(&res->r_actions, act, depth, timeout, interval) != 0) + free(act); + } while (1); +} + +/** + Try to load all the attributes in our rule set. If none are found, + or an error occurs, return NULL and move on to the next one. + + @param rule Resource rule set to use when looking for data + @param base Base XPath path to start with. + @return New resource if legal or NULL on failure/error + */ +resource_t * +load_resource(resource_rule_t * rule, const char *base) +{ + resource_t *res; + char ccspath[1024]; + char *attrname, *attr; + int x, found = 0, flags; + + res = malloc(sizeof(*res)); + if (!res) { + fprintf(stderr, "Out of memory\n"); + return NULL; + } + + memset(res, 0, sizeof(*res)); + res->r_rule = rule; + + for (x = 0; res->r_rule->rr_attrs && res->r_rule->rr_attrs[x].ra_name; x++) { + + flags = rule->rr_attrs[x].ra_flags; + attrname = strdup(rule->rr_attrs[x].ra_name); + if (!attrname) { + destroy_resource(res); + return NULL; + } + + /* + Ask CCS for the respective attribute + */ + attr = NULL; + snprintf(ccspath, sizeof(ccspath), "%s/@%s", base, attrname); + + if (conf_get(ccspath, &attr) != 0) { + + if (flags & (RA_REQUIRED | RA_PRIMARY)) { + /* Missing required attribute. We're done. */ + free(attrname); + destroy_resource(res); + return NULL; + } + + if (!(flags & RA_INHERIT)) { + /* + If we don't have the inherit flag, see if + we have a value anyway. If we do, + this value is the default value, and + should be used. + */ + if (!rule->rr_attrs[x].ra_value) { + free(attrname); + continue; + } + + /* Copy default value from resource rule */ + attr = strdup(rule->rr_attrs[x].ra_value); + } + } + + found = 1; + + /* + If we are supposed to inherit and we don't have an + instance of the specified attribute in CCS, then we + keep the inherit flag and use it as the attribute. + + However, if we _do_ have the attribute for this instance, + we drop the inherit flag and use the attribute. + */ + if (flags & RA_INHERIT) { + if (attr) { + flags &= ~RA_INHERIT; + } else { + attr = strdup(rule->rr_attrs[x].ra_value); + if (!attr) { + destroy_resource(res); + free(attrname); + return NULL; + } + } + } + + /* + Store the attribute. We'll ensure all required + attributes are present soon. + */ + if (attrname && attr) + store_attribute(&res->r_attrs, attrname, attr, flags); + } + + if (!found) { + destroy_resource(res); + return NULL; + } + + res->r_actions = act_dup(rule->rr_actions); + _get_actions_ccs(base, res); + + return res; +} + +/** + Read all resources in the resource manager block in CCS. + + @param reslist Empty list to fill with resources. + @param rulelist List of rules to use when searching CCS. + @return 0 on success, nonzero on failure. + */ +int +load_resources(resource_t ** reslist, resource_rule_t ** rulelist) +{ + int resID = 0; + resource_t *newres; + resource_rule_t *currule; + char tok[256]; + + list_do(rulelist, currule) { + + for (resID = 1;; resID++) { + snprintf(tok, sizeof(tok), RESOURCE_BASE "/%s[%d]", currule->rr_type, resID); + + newres = load_resource(currule, tok); + if (!newres) + break; + + if (store_resource(reslist, newres) != 0) { + fprintf(stderr, "Error storing %s resource\n", newres->r_rule->rr_type); + + destroy_resource(newres); + } + + /* Just information */ + newres->r_flags = RF_DEFINED; + } + } + while (!list_done(rulelist, currule)) ; + + return 0; +} diff --git a/__root__/ccs-flatten/reslist.h b/__root__/ccs-flatten/reslist.h new file mode 100644 index 0000000..07c84fe --- /dev/null +++ b/__root__/ccs-flatten/reslist.h @@ -0,0 +1,205 @@ +/* + Copyright Red Hat, Inc. 2004 + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ +#ifndef _RESLIST_H +# define _RESLIST_H + +# include <stdint.h> +# include <libxml/parser.h> +# include <libxml/xmlmemory.h> +# include <libxml/xpath.h> + +# define RA_PRIMARY (1<<0) /** Primary key */ +# define RA_UNIQUE (1<<1) /** Unique for given type */ +# define RA_REQUIRED (1<<2) /** Required (or an error if not present */ +# define RA_INHERIT (1<<3) /** Inherit a parent resource's attr */ +# define RA_RECONFIG (1<<4) /** Allow inline reconfiguration */ + +# define RF_INLINE (1<<0) +# define RF_DEFINED (1<<1) +# define RF_NEEDSTART (1<<2) /** Used when adding/changing resources */ +# define RF_NEEDSTOP (1<<3) /** Used when deleting/changing resources */ +# define RF_COMMON (1<<4) /** " */ +# define RF_INDEPENDENT (1<<5) + /** Define this for a resource if it is + otherwise an independent subtree */ +# define RF_RECONFIG (1<<6) + +# define RF_INIT (1<<7) + /** Resource rule: Initialize this resource + class on startup */ +# define RF_DESTROY (1<<8) /** Resource rule flag: Destroy this + resource class if you delete it from + the configuration */ +# define RF_ENFORCE_TIMEOUTS (1<<9) + /** Enforce timeouts for this node */ +# define RF_NON_CRITICAL (1<<10) + /** stop this resource if it fails */ +# define RF_QUIESCE (1<<11) /** don't restart this resource */ + +# define RES_STOPPED (0) +# define RES_STARTED (1) +# define RES_FAILED (2) +# define RES_DISABLED (3) + +# ifndef SHAREDIR +# define SHAREDIR "/usr/share/cluster" +# endif + +# define RESOURCE_ROOTDIR SHAREDIR +# define RESOURCE_TREE_ROOT "//rm" +# define RESOURCE_BASE RESOURCE_TREE_ROOT "/resources" +# define RESOURCE_ROOT_FMT RESOURCE_TREE_ROOT "/%s[%d]" + +# define RESOURCE_MAX_LEVELS 100 + +/* Include OCF definitions */ +//#include <res-ocf.h> + +typedef struct _resource_attribute { + char *ra_name; + char *ra_value; + int ra_flags; + int _pad_; +} resource_attr_t; + +typedef struct _resource_child { + char *rc_name; + int rc_startlevel; + int rc_stoplevel; + int rc_forbid; + int rc_flags; +} resource_child_t; + +typedef struct _resource_act { + char *ra_name; + time_t ra_timeout; + time_t ra_last; + time_t ra_interval; + int ra_depth; + int _pad_; +} resource_act_t; + +typedef struct _resource_rule { + list_head(); + char *rr_type; + char *rr_agent; + char *rr_version; /** agent XML spec version; OCF-ism */ + int rr_flags; + int rr_maxrefs; + resource_attr_t *rr_attrs; + resource_child_t *rr_childtypes; + resource_act_t *rr_actions; +} resource_rule_t; + +typedef struct _resource { + list_head(); + resource_rule_t *r_rule; + char *r_name; + resource_attr_t *r_attrs; + resource_act_t *r_actions; + int r_flags; + int r_refs; + int r_incarnations; /** Number of instances running locally */ + int _pad_; /* align */ +} resource_t; + +typedef struct _rg_node { + list_head(); + struct _rg_node *rn_child, *rn_parent; + resource_t *rn_resource; + resource_act_t *rn_actions; + int rn_state; /* State of this instance of rn_resource */ + int rn_flags; + int rn_last_status; + int rn_last_depth; + int rn_checked; + int rn_pad; +} resource_node_t; + +typedef struct _fod_node { + list_head(); + char *fdn_name; + int fdn_prio; + int fdn_nodeid; /* on rhel4 this will be 64-bit int */ +} fod_node_t; + +typedef struct _fod { + list_head(); + char *fd_name; + fod_node_t *fd_nodes; + int fd_flags; + int _pad_; /* align */ +} fod_t; + +/* + Exported Functions + */ +int res_flatten(xmlNode ** n, xmlNode * r, resource_node_t ** tree, resource_t * res); + +int expand_time(char *val); +int store_action(resource_act_t ** actsp, char *name, int depth, int timeout, int interval); + +/* + Load/kill resource rule sets + */ +int load_resource_rules(const char *rpath, resource_rule_t ** rules); +void destroy_resource_rules(resource_rule_t ** rules); + +/* + Load/kill resource sets + */ +int load_resources(resource_t ** reslist, resource_rule_t ** rulelist); +void dump_resources(FILE * fp, resource_t ** reslist); +void destroy_resources(resource_t ** list); + +/* + Construct/deconstruct resource trees + */ +int build_resource_tree(resource_node_t ** tree, + resource_rule_t ** rulelist, resource_t ** reslist); +void destroy_resource_tree(resource_node_t ** tree); + +/* + Construct/deconstruct failover domains + */ +int construct_domains(fod_t ** domains); +void deconstruct_domains(fod_t ** domains); + +/* + Handy functions + */ +resource_t *find_resource_by_ref(resource_t ** reslist, char *type, char *ref); +resource_rule_t *find_rule_by_type(resource_rule_t ** rulelist, char *type); +void res_build_name(char *, size_t, resource_t *); + +/* + Internal functions; shouldn't be needed. + */ +int store_attribute(resource_attr_t ** attrsp, char *name, char *value, int flags); + +resource_t *load_resource(resource_rule_t * rule, const char *base); +int store_resource(resource_t ** reslist, resource_t * newres); +void destroy_resource(resource_t * res); + +char *attr_value(resource_node_t * node, const char *attrname); +char *res_attr_value(resource_t * res, const char *attrname); +char *primary_attr_value(resource_t *); +void *act_dup(resource_act_t * acts); + +#endif /* _RESLIST_H */ diff --git a/__root__/ccs-flatten/resrules.c b/__root__/ccs-flatten/resrules.c new file mode 100644 index 0000000..c0b670d --- /dev/null +++ b/__root__/ccs-flatten/resrules.c @@ -0,0 +1,971 @@ +/* + Copyright Red Hat, Inc. 2004-2010 + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ +#include <libxml/parser.h> +#include <libxml/xmlmemory.h> +#include <libxml/xpath.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <resgroup.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <list.h> +#include <ctype.h> +#include <reslist.h> +#include <dirent.h> +#include <libgen.h> +#include <sys/wait.h> +#include <xmlconf.h> + +/** + Store a new resource rule in the given rule list. + + @param rulelist List of rules to store new rule in. + @param newrule New rule to store. + @return 0 on success or -1 if rule with same name + already exists in rulelist + */ +static int +store_rule(resource_rule_t ** rulelist, resource_rule_t * newrule) +{ + resource_rule_t *curr; + + list_do(rulelist, curr) { + if (!strcmp(newrule->rr_type, curr->rr_type)) { + fprintf(stderr, "Error storing %s: Duplicate\n", newrule->rr_type); + return -1; + } + + } + while (!list_done(rulelist, curr)) ; + + list_insert(rulelist, newrule); + return 0; +} + +/** + Obliterate a resource_rule_t structure. + + @param rr Resource rule to free. + */ +static void +destroy_resource_rule(resource_rule_t * rr) +{ + int x; + + if (rr->rr_type) + free(rr->rr_type); + if (rr->rr_agent) + free(rr->rr_agent); + if (rr->rr_version) + free(rr->rr_version); + + if (rr->rr_attrs) { + for (x = 0; rr->rr_attrs && rr->rr_attrs[x].ra_name; x++) { + free(rr->rr_attrs[x].ra_name); + if (rr->rr_attrs[x].ra_value) + free(rr->rr_attrs[x].ra_value); + } + + free(rr->rr_attrs); + } + + if (rr->rr_actions) { + for (x = 0; rr->rr_actions && rr->rr_actions[x].ra_name; x++) { + free(rr->rr_actions[x].ra_name); + } + + free(rr->rr_actions); + } + + if (rr->rr_childtypes) { + for (x = 0; rr->rr_childtypes && rr->rr_childtypes[x].rc_name; x++) + free(rr->rr_childtypes[x].rc_name); + free(rr->rr_childtypes); + } + + free(rr); +} + +/** + Destroy a list of resource rules. + + @param rules List of rules to destroy. + */ +void +destroy_resource_rules(resource_rule_t ** rules) +{ + resource_rule_t *rr; + + while ((rr = *rules)) { + list_remove(rules, rr); + destroy_resource_rule(rr); + } +} + +/** + Get and store the maxparents (max instances) attribute for a given + resource rule set. + + @param doc Pre-parsed XML document pointer. + @param ctx Pre-allocated XML XPath context pointer. + @param base XPath prefix to search + @param rr Resource rule to store new information in. + */ +static void +_get_maxparents(xmlDocPtr doc, xmlXPathContextPtr ctx, char *base, resource_rule_t * rr) +{ + char xpath[256]; + char *ret = NULL; + + snprintf(xpath, sizeof(xpath), "%s/attributes/@maxinstances", base); + ret = xpath_get_one(doc, ctx, xpath); + if (ret) { + rr->rr_maxrefs = atoi(ret); + if (rr->rr_maxrefs < 0) + rr->rr_maxrefs = 0; + free(ret); + } +} + +/** + Get and store a bit field. + + @param doc Pre-parsed XML document pointer. + @param ctx Pre-allocated XML XPath context pointer. + @param base XPath prefix to search + @param rr Resource rule to store new information in. + */ +static void +_get_rule_flag(xmlDocPtr doc, xmlXPathContextPtr ctx, const char *base, + resource_rule_t * rr, const char *flag, int bit) +{ + char xpath[256]; + char *ret = NULL; + + snprintf(xpath, sizeof(xpath), "%s/attributes/@%s", base, flag); + ret = xpath_get_one(doc, ctx, xpath); + if (ret) { + if (atoi(ret)) { + rr->rr_flags |= bit; + } else { + rr->rr_flags &= ~bit; + } + free(ret); + } +} + +/** + Get and store the version + + @param doc Pre-parsed XML document pointer. + @param ctx Pre-allocated XML XPath context pointer. + @param base XPath prefix to search + @param rr Resource rule to store new information in. + */ +static void +_get_version(xmlDocPtr doc, xmlXPathContextPtr ctx, char *base, resource_rule_t * rr) +{ + char xpath[256]; + char *ret = NULL; + + snprintf(xpath, sizeof(xpath), "%s/@version", base); + ret = xpath_get_one(doc, ctx, xpath); + if (ret) { + rr->rr_version = ret; + free(ret); + } + rr->rr_version = NULL; +} + +int +expand_time(char *val) +{ + int curval, len; + int ret = 0; + char *start = val, ival[16]; + + if (!val) + return (time_t) 0; + + while (start[0]) { + + len = 0; + curval = 0; + memset(ival, 0, sizeof(ival)); + + while (isdigit(start[len])) { + ival[len] = start[len]; + len++; + } + + if (len) { + curval = atoi(ival); + } else { + len = 1; + } + + switch (start[len]) { + case 0: + case 'S': + case 's': + break; + case 'M': + case 'm': + curval *= 60; + break; + case 'h': + case 'H': + curval *= 3600; + break; + case 'd': + case 'D': + curval *= 86400; + break; + case 'w': + case 'W': + curval *= 604800; + break; + case 'y': + case 'Y': + curval *= 31536000; + break; + default: + curval = 0; + } + + ret += (time_t) curval; + start += len; + } + + return ret; +} + +/** + * Store a resource action + * @param actsp Action array; may be modified and returned! + * @param name Name of the action + * @param depth Resource depth (status/monitor; -1 means *ALL LEVELS* + * ... this means that only the highest-level check depth + * will ever be performed!) + * @param timeout Timeout (not used) + * @param interval Time interval for status/monitor + * @return 0 on success, -1 on failure + * + */ +int +store_action(resource_act_t ** actsp, char *name, int depth, int timeout, int interval) +{ + int x = 0, replace = 0; + resource_act_t *acts = *actsp; + + if (!name) + return -1; + + if (depth < 0 && timeout < 0 && interval < 0) + return -1; + + if (!acts) { + /* Can't create with anything < 0 */ + if (depth < 0 || timeout < 0 || interval < 0) + return -1; + + acts = malloc(sizeof(resource_act_t) * 2); + if (!acts) + return -1; + acts[0].ra_name = name; + acts[0].ra_depth = depth; + acts[0].ra_timeout = timeout; + acts[0].ra_interval = interval; + acts[0].ra_last = 0; + acts[1].ra_name = NULL; + + *actsp = acts; + return 0; + } + + for (x = 0; acts[x].ra_name; x++) { + if (!strcmp(acts[x].ra_name, name) && (depth == acts[x].ra_depth || depth == -1)) { + fprintf(stderr, "Replacing action '%s' depth %d: ", name, acts[x].ra_depth); + if (timeout >= 0) { + fprintf(stderr, "timeout: %d->%d ", (int)acts[x].ra_timeout, (int)timeout); + acts[x].ra_timeout = timeout; + } + if (interval >= 0) { + fprintf(stderr, "interval: %d->%d", (int)acts[x].ra_interval, (int)interval); + acts[x].ra_interval = interval; + } + fprintf(stderr, "\n"); + replace = 1; + } + } + + if (replace) + /* If we replaced something, we're done */ + return 1; + + /* Can't create with anything < 0 */ + if (depth < 0 || timeout < 0 || interval < 0) + return -1; + + acts = realloc(acts, sizeof(resource_act_t) * (x + 2)); + if (!acts) + return -1; + + acts[x].ra_name = name; + acts[x].ra_depth = depth; + acts[x].ra_timeout = timeout; + acts[x].ra_interval = interval; + acts[x].ra_last = 0; + + acts[x + 1].ra_name = NULL; + + *actsp = acts; + return 0; +} + +static void +_get_actions(xmlDocPtr doc, xmlXPathContextPtr ctx, char *base, resource_rule_t * rr) +{ + char xpath[256]; + int idx = 0; + char *act, *ret; + int interval, timeout, depth; + + do { + interval = 0; + depth = 0; + act = NULL; + timeout = 0; + + snprintf(xpath, sizeof(xpath), "%s/action[%d]/@name", base, ++idx); + + act = xpath_get_one(doc, ctx, xpath); + if (!act) + break; + + snprintf(xpath, sizeof(xpath), "%s/action[%d]/@timeout", base, idx); + ret = xpath_get_one(doc, ctx, xpath); + if (ret) { + timeout = expand_time(ret); + if (timeout < 0) + timeout = 0; + free(ret); + } + + snprintf(xpath, sizeof(xpath), "%s/action[%d]/@interval", base, idx); + ret = xpath_get_one(doc, ctx, xpath); + if (ret) { + interval = expand_time(ret); + if (interval < 0) + interval = 0; + free(ret); + } + + if (!strcmp(act, "status") || !strcmp(act, "monitor")) { + snprintf(xpath, sizeof(xpath), "%s/action[%d]/@depth", base, idx); + ret = xpath_get_one(doc, ctx, xpath); + if (ret) { + depth = atoi(ret); + if (depth < 0) + depth = 0; + free(ret); + } + } + + if (store_action(&rr->rr_actions, act, depth, timeout, interval) != 0) + free(act); + } while (1); +} + +/** + Store an attribute with the given name, value, and flags in a resource_t + structure. + XXX This could be rewritten to use the list macros. + + @param attrsp Attribute array to store new attribute in. + @param name Name of attribute (must be non-null) + @param value Value of attribute + @param flags Attribute flags, or 0 if none. + @return 0 on success, nonzero on error/failure + */ +int +store_attribute(resource_attr_t ** attrsp, char *name, char *value, int flags) +{ + int x = 0; + resource_attr_t *attrs = *attrsp; + + if (!name) + return -1; + + if (!attrs) { + attrs = malloc(sizeof(resource_attr_t) * 2); + if (!attrs) + return -1; + attrs[0].ra_name = name; + attrs[0].ra_value = value; + attrs[0].ra_flags = flags; + attrs[1].ra_name = NULL; + attrs[1].ra_value = NULL; + + *attrsp = attrs; + return 0; + } + + for (x = 0; attrs[x].ra_name; x++) ; + + attrs = realloc(attrs, sizeof(resource_attr_t) * (x + 2)); + if (!attrs) + return -1; + + /* Primary attribute goes first. This makes this interaction + with CCS work way faster. */ + if (flags & RA_PRIMARY) { + attrs[x].ra_name = attrs[0].ra_name; + attrs[x].ra_value = attrs[0].ra_value; + attrs[x].ra_flags = attrs[0].ra_flags; + attrs[0].ra_name = name; + attrs[0].ra_value = value; + attrs[0].ra_flags = flags; + } else { + attrs[x].ra_name = name; + attrs[x].ra_value = value; + attrs[x].ra_flags = flags; + } + attrs[x + 1].ra_name = NULL; + attrs[x + 1].ra_value = NULL; + + *attrsp = attrs; + return 0; +} + +/** + Store a child type in the child array of a resource rule. + XXX Could be rewritten to use list macros. + + @param childp Child array. Might be modified. + @param name Name of child type + @param start Start level + @param stop Stop level + @param forbid Do NOT allow this child type to exist + @param flags set to 1 to note that it was defined inline + @return 0 on success, nonzero on failure + */ +static int +store_childtype(resource_child_t ** childp, char *name, int start, int stop, int forbid, int flags) +{ + int x = 0; + resource_child_t *child = *childp; + + if (!name) + return -1; + + if (!child) { + child = malloc(sizeof(resource_child_t) * 2); + if (!child) + return -1; + child[0].rc_name = name; + child[0].rc_startlevel = start; + child[0].rc_stoplevel = stop; + child[0].rc_forbid = forbid; + child[0].rc_flags = flags; + child[1].rc_name = NULL; + + *childp = child; + return 0; + } + + for (x = 0; child[x].rc_name; x++) ; + + child = realloc(child, sizeof(resource_child_t) * (x + 2)); + if (!child) + return -1; + + child[x].rc_name = name; + child[x].rc_startlevel = start; + child[x].rc_stoplevel = stop; + child[x].rc_forbid = forbid; + child[x].rc_flags = flags; + child[x + 1].rc_name = NULL; + + *childp = child; + return 0; +} + +/** + Get and store attributes for a given instance of a resource rule. + + @param doc Pre-parsed XML document pointer. + @param ctx Pre-allocated XML XPath context pointer. + @param base XPath prefix to search + @param rr Resource rule to store new information in. + @return 0 + */ +static int +_get_rule_attrs(xmlDocPtr doc, xmlXPathContextPtr ctx, const char *base, resource_rule_t * rr) +{ + char *ret, *attrname, *dflt = NULL, xpath[256]; + int x, flags, primary_found = 0; + + for (x = 1; 1; x++) { + snprintf(xpath, sizeof(xpath), "%s/parameter[%d]/@name", base, x); + + ret = xpath_get_one(doc, ctx, xpath); + if (!ret) + break; + + flags = 0; + attrname = ret; + + /* + See if there's a default value. + */ + snprintf(xpath, sizeof(xpath), "%s/parameter[%d]/content/@default", base, x); + dflt = xpath_get_one(doc, ctx, xpath); + + /* + See if this is either the primary identifier or + a required field. + */ + snprintf(xpath, sizeof(xpath), "%s/parameter[%d]/@required", base, x); + if ((ret = xpath_get_one(doc, ctx, xpath))) { + if ((atoi(ret) != 0) || (ret[0] == 'y')) + flags |= RA_REQUIRED; + free(ret); + } + + /* + See if this is supposed to be unique + */ + snprintf(xpath, sizeof(xpath), "%s/parameter[%d]/@unique", base, x); + if ((ret = xpath_get_one(doc, ctx, xpath))) { + if ((atoi(ret) != 0) || (ret[0] == 'y')) + flags |= RA_UNIQUE; + free(ret); + } + + snprintf(xpath, sizeof(xpath), "%s/parameter[%d]/@primary", base, x); + if ((ret = xpath_get_one(doc, ctx, xpath))) { + if ((atoi(ret) != 0) || (ret[0] == 'y')) { + if (primary_found) { + free(ret); + fprintf(stderr, "Multiple primary " + "definitions for " "resource type %s\n", rr->rr_type); + return -1; + } + flags |= RA_PRIMARY; + primary_found = 1; + } + free(ret); + } + + /* + See if this can be reconfigured on the fly without a + stop/start + */ + snprintf(xpath, sizeof(xpath), "%s/parameter[%d]/@reconfig", base, x); + if ((ret = xpath_get_one(doc, ctx, xpath))) { + if ((atoi(ret) != 0) || (ret[0] == 'y')) + flags |= RA_RECONFIG; + free(ret); + } + + /* + See if this is supposed to be inherited + */ + snprintf(xpath, sizeof(xpath), "%s/parameter[%d]/@inherit", base, x); + if ((ret = xpath_get_one(doc, ctx, xpath))) { + flags |= RA_INHERIT; + + if (flags & (RA_REQUIRED | RA_PRIMARY | RA_UNIQUE)) { + free(ret); + fprintf(stderr, "Can not inherit and be primary, " "unique, or required\n"); + return -1; + } + /* + don't free ret. Store as attr value. If we had + a default value specified from above, free it; + inheritance supercedes a specified default value. + */ + if (dflt) + free(dflt); + } else { + /* + Use default value, if specified, as the attribute + value. + */ + ret = dflt; + } + + /* + Store the attribute. We'll ensure all required + attributes are present soon. + */ + if (attrname) + store_attribute(&rr->rr_attrs, attrname, ret, flags); + } + + return 0; +} + +/** + Get and store attributes for a given instance of a resource. + + @param doc Pre-parsed XML document pointer. + @param ctx Pre-allocated XML XPath context pointer. + @param base XPath prefix to search + @param rr Resource rule to store new information in. + @return 0 + */ +static int +_get_childtypes(xmlDocPtr doc, xmlXPathContextPtr ctx, char *base, resource_rule_t * rr) +{ + char *ret, *childname, xpath[256]; + int x, startlevel = 0, stoplevel = 0, forbid = 0; + + for (x = 1; 1; x++) { + snprintf(xpath, sizeof(xpath), "%s/child[%d]/@type", base, x); + + ret = xpath_get_one(doc, ctx, xpath); + if (!ret) + break; + + startlevel = stoplevel = forbid = 0; + childname = ret; + + /* + Try to get the start level if it exists + */ + snprintf(xpath, sizeof(xpath), "%s/child[%d]/@start", base, x); + if ((ret = xpath_get_one(doc, ctx, xpath))) { + startlevel = atoi(ret); + free(ret); + } + + /* + Try to get the stop level if it exists + */ + snprintf(xpath, sizeof(xpath), "%s/child[%d]/@stop", base, x); + if ((ret = xpath_get_one(doc, ctx, xpath))) { + stoplevel = atoi(ret); + free(ret); + } + + /* + Get the 'forbidden' flag if it exists + */ + snprintf(xpath, sizeof(xpath), "%s/child[%d]/@forbid", base, x); + if ((ret = xpath_get_one(doc, ctx, xpath))) { + forbid = atoi(ret); + free(ret); + } + + /* + Store the attribute. We'll ensure all required + attributes are present soon. + */ + if (childname) + store_childtype(&rr->rr_childtypes, childname, startlevel, stoplevel, forbid, 0); + } + + return 0; +} + +/** + Read a file from a stdout pipe. + */ +static int +read_pipe(int fd, char **file, size_t * length) +{ + char buf[4096]; + int n, done = 0; + + *file = NULL; + *length = 0; + + while (!done) { + + n = read(fd, buf, sizeof(buf)); + if (n < 0) { + + if (errno == EINTR) + continue; + + if (*file) + free(*file); + return -1; + } + + if (n == 0 && (!*length)) + return 0; + + if (n == 0) { + done = 1; + } + + if (*file) + *file = realloc(*file, (*length) + n + done); + else + *file = malloc(n + done); + + if (!*file) + return -1; + + memcpy((*file) + (*length), buf, n); + *length += (done + n); + } + + /* Null terminator */ + (*file)[(*length) - 1] = 0; + + return 0; +} + +static xmlDocPtr +read_resource_agent_metadata(char *filename) +{ + int pid; + int _pipe[2]; + char *data; + size_t size; + xmlDocPtr doc; + + if (pipe(_pipe) == -1) + return NULL; + + pid = fork(); + if (pid == -1) { + close(_pipe[0]); + close(_pipe[1]); + } + + if (pid == 0) { + /* child */ + close(0); + close(1); + close(2); + + close(_pipe[0]); + dup2(_pipe[1], 1); + close(_pipe[1]); + + /* exec */ + execl(filename, filename, "meta-data", NULL); + exit(1); + } + + close(_pipe[1]); + /* parent */ + if (read_pipe(_pipe[0], &data, &size) == -1) { + close(_pipe[0]); + return NULL; + } + + waitpid(pid, NULL, 0); + close(_pipe[0]); + + if (!size) + return NULL; + + doc = xmlParseMemory(data, size); + free(data); + return doc; +} + +/** + Load the XML rule set for a resource and store attributes, constructing + a new resource_t structure. + + @param filename File name to load rules from + @param rules Rule list to add new rules to + @return 0 + */ +static int +load_resource_rulefile(char *filename, resource_rule_t ** rules) +{ + resource_rule_t *rr = NULL; + xmlDocPtr doc = NULL; + xmlXPathContextPtr ctx = NULL; + int ruleid = 0; + char *type; + char base[256]; + + doc = read_resource_agent_metadata(filename); + if (!doc) + return 0; + ctx = xmlXPathNewContext(doc); + + do { + /* Look for resource types */ + snprintf(base, sizeof(base), "/resource-agent[%d]/@name", ++ruleid); + type = xpath_get_one(doc, ctx, base); + if (!type) + break; + + if (!strcasecmp(type, "action")) { + fprintf(stderr, "Error: Resource type '%s' is reserved", type); + free(type); + break; + } + + rr = malloc(sizeof(*rr)); + if (!rr) + break; + memset(rr, 0, sizeof(*rr)); + + rr->rr_flags = RF_INIT | RF_DESTROY; + rr->rr_type = type; + snprintf(base, sizeof(base), "/resource-agent[%d]", ruleid); + + /* + First, grab the global attributes if existent + */ + _get_version(doc, ctx, base, rr); + + snprintf(base, sizeof(base), "/resource-agent[%d]/special[@tag=\"rgmanager\"]", ruleid); + _get_maxparents(doc, ctx, base, rr); + _get_rule_flag(doc, ctx, base, rr, "init_on_add", RF_INIT); + _get_rule_flag(doc, ctx, base, rr, "destroy_on_delete", RF_DESTROY); + rr->rr_agent = strdup(filename); + + /* + Second, add the children fields + */ + _get_childtypes(doc, ctx, base, rr); + + /* + Get the OCF status check intervals/monitor. + */ + snprintf(base, sizeof(base), "/resource-agent[%d]/actions", ruleid); + _get_actions(doc, ctx, base, rr); + + /* + Last, load the attributes from our XML file and their + respective instantiations from CCS + */ + snprintf(base, sizeof(base), "/resource-agent[%d]/parameters", ruleid); + if (_get_rule_attrs(doc, ctx, base, rr) < 0) { + destroy_resource_rule(rr); + rr = NULL; + } + + if (!rr) + continue; + + if (store_rule(rules, rr) != 0) { + destroy_resource_rule(rr); + rr = NULL; + } + } while (1); + + if (ctx) + xmlXPathFreeContext(ctx); + if (doc) + xmlFreeDoc(doc); + + return 0; +} + +/** + Load all the resource rules we can find from our resource root + directory. + + @param rules Rule list to create/add to + @return 0 on success, -1 on failure. Sucess does not + imply any rules have been found; only that no + errors were encountered. + */ +int +load_resource_rules(const char *rpath, resource_rule_t ** rules) +{ + DIR *dir; + struct dirent *de; + char *fn, *dot; + char path[2048]; + struct stat st_buf; + + dir = opendir(rpath); + if (!dir) + return -1; + + xmlInitParser(); + while ((de = readdir(dir))) { + + fn = basename(de->d_name); + if (!fn) + continue; + + /* Ignore files with common backup extension */ + if ((fn != NULL) && (strlen(fn) > 0) && (fn[strlen(fn) - 1] == '~')) + continue; + + /* Ignore hidden files */ + if (*fn == '.') + continue; + + dot = strrchr(fn, '.'); + if (dot) { + /* Ignore RPM installed save files, patches, + diffs, etc. */ + if (!strncasecmp(dot, ".rpm", 4)) { + fprintf(stderr, "Warning: " + "Ignoring %s/%s: Bad extension %s\n", rpath, de->d_name, dot); + continue; + } + } + + snprintf(path, sizeof(path), "%s/%s", rpath, de->d_name); + + if (stat(path, &st_buf) < 0) + continue; + + if (S_ISDIR(st_buf.st_mode)) + continue; + + if (st_buf.st_mode & (S_IXUSR | S_IXOTH | S_IXGRP)) { + //printf("Loading resource rule from %s\n", path); + load_resource_rulefile(path, rules); + } + } + + closedir(dir); + + return 0; +} + +/** + Find a resource rule given its type. + + @param rulelist Rule list to search + @param type Rule type identifier + @return Resource rule or NULL if not found. + */ +resource_rule_t * +find_rule_by_type(resource_rule_t ** rulelist, char *type) +{ + resource_rule_t *curr = NULL; + + list_do(rulelist, curr) { + if (!strcmp(curr->rr_type, type)) + return curr; + } + while (!list_done(rulelist, curr)) ; + + return NULL; +} diff --git a/__root__/ccs-flatten/restree.c b/__root__/ccs-flatten/restree.c new file mode 100644 index 0000000..2a00427 --- /dev/null +++ b/__root__/ccs-flatten/restree.c @@ -0,0 +1,723 @@ +/* + Copyright Red Hat, Inc. 2004-2006 + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + Fix for #193859 - relocation of a service w/o umounting file-systems + by Navid Sheikhol-Eslami [ navid at redhat dot com ] +*/ +#include <libxml/parser.h> +#include <libxml/xmlmemory.h> +#include <libxml/xpath.h> +#include <stdlib.h> +#include <stdio.h> +#include <list.h> +#include <sys/wait.h> +#include <resgroup.h> +#include <libgen.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <reslist.h> +#include <assert.h> +#include <xmlconf.h> + +/* XXX from resrules.c */ +int store_childtype(resource_child_t ** childp, char *name, int start, + int stop, int forbid, int flags); +int _res_op(xmlNode ** xpp, xmlNode * rmp, resource_node_t ** tree, resource_t * first, char *type); +static inline int + + +_res_op_internal(xmlNode ** xpp, xmlNode * rmp, resource_node_t ** tree, resource_t * first, + char *type, resource_node_t * node); + +/* XXX from reslist.c */ +void *act_dup(resource_act_t * acts); + +/** + Fold a resource into an XML node. + + @param xpp XML node pp + @param rmp resources block pp + @param node Resource tree node we're dealing with + @param op Operation to perform (stop/start/etc.) + @param depth OCF Check level/depth + @return Return value of script. + @see build_env + */ +static int +res_do_flatten(xmlNode ** xpp, xmlNode * rmp, resource_node_t * node, const char *arg, int depth) +{ + xmlNode *n, *r; + resource_attr_t *ra; + resource_t *res = node->rn_resource; + char *val; + char buf[256]; + int x, y; + + n = xmlNewNode(NULL, (xmlChar *) node->rn_resource->r_rule->rr_type); + + xmlSetProp(n, (xmlChar *) "rgmanager-meta-agent", + (xmlChar *) basename(node->rn_resource->r_rule->rr_agent)); + + /* Multiple-instance resources must be decomposed into separate + resources */ + if (node->rn_resource->r_refs > 1) { + snprintf(buf, sizeof(buf), "%s_%d", + primary_attr_value(node->rn_resource), node->rn_resource->r_incarnations); + ++node->rn_resource->r_incarnations; + } else { + snprintf(buf, sizeof(buf), "%s", primary_attr_value(node->rn_resource)); + } + + for (x = 0; node->rn_resource->r_attrs && node->rn_resource->r_attrs[x].ra_name; x++) { + ra = &node->rn_resource->r_attrs[x]; + + if (ra->ra_flags & RA_PRIMARY) { + xmlSetProp(n, (xmlChar *) ra->ra_name, (xmlChar *) buf); + } else { + val = attr_value(node, res->r_attrs[x].ra_name); + if (!val) + continue; + + for (y = 0; res->r_rule->rr_attrs[y].ra_name; y++) { + if (strcmp(ra->ra_name, res->r_rule->rr_attrs[y].ra_name)) + continue; + + if (!res->r_rule->rr_attrs[y].ra_value || + strcmp(val, res->r_rule->rr_attrs[y].ra_value)) + xmlSetProp(n, (xmlChar *) ra->ra_name, (xmlChar *) val); + } + } + } + + if (!*xpp) { + /* Add top-level container */ + *xpp = n; + } else { + if (!rmp) { + xmlAddChild(*xpp, n); + } else { + r = xmlNewNode(NULL, (xmlChar *) node->rn_resource->r_rule->rr_type); + xmlSetProp(r, (xmlChar *) "ref", (xmlChar *) primary_attr_value(node->rn_resource)); + xmlAddChild(rmp, n); + xmlAddChild(*xpp, r); + } + } + + return 0; +} + +static inline void +assign_restart_policy(resource_t * curres, resource_node_t * parent, + resource_node_t * node, char *base) +{ + char *val; + int max_restarts = 0; + time_t restart_expire_time = 0; + char tok[1024]; + + if (!curres || !node) + return; + if (parent && !(node->rn_flags & RF_INDEPENDENT)) + return; + + if (node->rn_flags & RF_INDEPENDENT) { + /* per-resource-node failures / expire times */ + snprintf(tok, sizeof(tok), "%s/@__max_restarts", base); + if (conf_get(tok, &val) == 0) { + max_restarts = atoi(val); + if (max_restarts <= 0) + max_restarts = 0; + free(val); + } + + snprintf(tok, sizeof(tok), "%s/@__restart_expire_time", base); + if (conf_get(tok, &val) == 0) { + restart_expire_time = (time_t) expand_time(val); + if ((int64_t) restart_expire_time <= 0) + restart_expire_time = 0; + free(val); + } + //if (restart_expire_time == 0 || max_restarts == 0) + return; + //goto out_assign; + } + + val = (char *)res_attr_value(curres, "max_restarts"); + if (!val) + return; + max_restarts = atoi(val); + if (max_restarts <= 0) + return; + val = res_attr_value(curres, "restart_expire_time"); + if (val) { + restart_expire_time = (time_t) expand_time(val); + if ((int64_t) restart_expire_time < 0) + return; + } +//out_assign: + return; +} + +static inline int +do_load_resource(char *base, + resource_rule_t * rule, + resource_node_t ** tree, + resource_t ** reslist, resource_node_t * parent, resource_node_t ** newnode) +{ + char tok[512]; + char *ref; + resource_node_t *node; + resource_t *curres; + time_t failure_expire = 0; + int max_failures = 0; + + snprintf(tok, sizeof(tok), "%s/@ref", base); + + if (conf_get(tok, &ref) != 0) { + /* There wasn't an existing resource. See if there + is one defined inline */ + curres = load_resource(rule, base); + if (!curres) { + /* No ref and no new one inline == + no more of the selected type */ + return 1; + } + + if (store_resource(reslist, curres) != 0) { + fprintf(stderr, "Error storing %s resource\n", curres->r_rule->rr_type); + destroy_resource(curres); + return -1; + } + + curres->r_flags = RF_INLINE; + + } else { + + curres = find_resource_by_ref(reslist, rule->rr_type, ref); + if (!curres) { + fprintf(stderr, "Error: Reference to nonexistent " + "resource %s (type %s)\n", ref, rule->rr_type); + free(ref); + return -1; + } + + if (curres->r_flags & RF_INLINE) { + fprintf(stderr, "Error: Reference to inlined " + "resource %s (type %s) is illegal\n", ref, rule->rr_type); + free(ref); + return -1; + } + free(ref); + } + + /* Load it if its max refs hasn't been exceeded */ + if (rule->rr_maxrefs && (curres->r_refs >= rule->rr_maxrefs)) { + fprintf(stderr, "Warning: Max references exceeded for resource" + " %s (type %s)\n", curres->r_attrs[0].ra_name, rule->rr_type); + return -1; + } + + node = malloc(sizeof(*node)); + if (!node) + return -1; + + memset(node, 0, sizeof(*node)); + + //printf("New resource tree node: %s:%s \n", curres->r_rule->rr_type,curres->r_attrs->ra_value); + + node->rn_child = NULL; + node->rn_parent = parent; + node->rn_resource = curres; + node->rn_state = RES_STOPPED; + node->rn_flags = 0; + node->rn_actions = (resource_act_t *) act_dup(curres->r_actions); + + if (parent) { + /* Independent subtree / non-critical for top-level is + * not useful and can interfere with restart thresholds for + * non critical resources */ + snprintf(tok, sizeof(tok), "%s/@__independent_subtree", base); + if (conf_get(tok, &ref) == 0) { + if (atoi(ref) == 1 || strcasecmp(ref, "yes") == 0) + node->rn_flags |= RF_INDEPENDENT; + if (atoi(ref) == 2 || strcasecmp(ref, "non-critical") == 0) { + curres->r_flags |= RF_NON_CRITICAL; + } + free(ref); + } + } + + snprintf(tok, sizeof(tok), "%s/@__enforce_timeouts", base); + if (conf_get(tok, &ref) == 0) { + if (atoi(ref) > 0 || strcasecmp(ref, "yes") == 0) + node->rn_flags |= RF_ENFORCE_TIMEOUTS; + free(ref); + } + + /* per-resource-node failures / expire times */ + snprintf(tok, sizeof(tok), "%s/@__max_failures", base); + if (conf_get(tok, &ref) == 0) { + max_failures = atoi(ref); + if (max_failures < 0) + max_failures = 0; + free(ref); + } + + snprintf(tok, sizeof(tok), "%s/@__failure_expire_time", base); + if (conf_get(tok, &ref) == 0) { + failure_expire = (time_t) expand_time(ref); + if ((int64_t) failure_expire < 0) + failure_expire = 0; + free(ref); + } + + if (max_failures && failure_expire) { + /* + node->rn_failure_counter = restart_init(failure_expire, + max_failures); + */ + } + + curres->r_refs++; + + if (curres->r_refs > 1 && (curres->r_flags & RF_NON_CRITICAL)) { + res_build_name(tok, sizeof(tok), curres); + fprintf(stderr, "Non-critical flag for %s is being cleared due to multiple references.\n", + tok); + curres->r_flags &= ~RF_NON_CRITICAL; + } + + if (curres->r_flags & RF_NON_CRITICAL) { + /* Independent subtree is implied if a + * resource is non-critical + */ + node->rn_flags |= RF_NON_CRITICAL | RF_INDEPENDENT; + + } + + assign_restart_policy(curres, parent, node, base); + + *newnode = node; + + list_insert(tree, node); + + return 0; +} + +/** + Build the resource tree. If a new resource is defined inline, add it to + the resource list. All rules, however, must have already been read in. + + @param tree Tree to modify/insert on to + @param parent Parent node, if one exists. + @param rule Rule surrounding the new node + @param rulelist List of all rules allowed in the tree. + @param reslist List of all currently defined resources + @param base Base CCS path. + @see destroy_resource_tree + */ +#define RFL_FOUND 0x1 +#define RFL_FORBID 0x2 +static int +build_tree(resource_node_t ** tree, + resource_node_t * parent, + resource_rule_t * rule, resource_rule_t ** rulelist, resource_t ** reslist, char *base) +{ + char tok[512]; + resource_rule_t *childrule; + resource_node_t *node; + char *ref; + char *tmp; + int ccount = 0, x = 0, y = 0, flags = 0; + + //printf("DESCEND: %s / %s\n", rule?rule->rr_type:"(none)", base); + + /* Pass 1: typed / defined children */ + for (y = 0; rule && rule->rr_childtypes && rule->rr_childtypes[y].rc_name; y++) { + + flags = 0; + list_for(rulelist, childrule, x) { + if (strcmp(rule->rr_childtypes[y].rc_name, childrule->rr_type)) + continue; + + flags |= RFL_FOUND; + + if (rule->rr_childtypes[y].rc_forbid) + flags |= RFL_FORBID; + + break; + } + + if (flags & RFL_FORBID) + /* Allow all *but* forbidden */ + continue; + + if (!(flags & RFL_FOUND)) + /* Not found? Wait for pass 2 */ + continue; + + //printf("looking for %s %s @ %s\n", + //rule->rr_childtypes[y].rc_name, + //childrule->rr_type, base); + for (x = 1;; x++) { + + /* Search for base/type[x]/@ref - reference an existing + resource */ + snprintf(tok, sizeof(tok), "%s/%s[%d]", base, childrule->rr_type, x); + + flags = 1; + switch (do_load_resource(tok, childrule, tree, reslist, parent, &node)) { + case -1: + continue; + case 1: + /* 1 == no more */ + //printf("No resource found @ %s\n", tok); + flags = 0; + break; + case 0: + break; + } + if (!flags) + break; + + /* Got a child :: bump count */ + snprintf(tok, sizeof(tok), "%s/%s[%d]", base, childrule->rr_type, x); + + /* Kaboom */ + build_tree(&node->rn_child, node, childrule, rulelist, reslist, tok); + + } + } + + /* Pass 2: untyped children */ + for (ccount = 1;; ccount++) { + snprintf(tok, sizeof(tok), "%s/child::*[%d]", base, ccount); + + if (conf_get(tok, &ref) != 0) { + /* End of the line. */ + //printf("End of the line: %s\n", tok); + break; + } + + tmp = strchr(ref, '='); + if (tmp) { + *tmp = 0; + } else { + /* no = sign... bad */ + free(ref); + continue; + } + + /* Find the resource rule */ + flags = 0; + list_for(rulelist, childrule, x) { + if (!strcasecmp(childrule->rr_type, ref)) { + /* Ok, matching rule found */ + flags = 1; + break; + } + } + /* No resource rule matching the child? Press on... */ + if (!flags) { + free(ref); + continue; + } + + flags = 0; + /* Don't descend on anything we should have already picked + up on in the above loop */ + for (y = 0; rule && rule->rr_childtypes && rule->rr_childtypes[y].rc_name; y++) { + /* SKIP defined child types of any type */ + if (strcmp(rule->rr_childtypes[y].rc_name, ref)) + continue; + if (rule->rr_childtypes[y].rc_flags == 0) { + /* 2 = defined as a real child */ + flags = 2; + break; + } + + flags = 1; + break; + } + + free(ref); + if (flags == 2) + continue; + + x = 1; + switch (do_load_resource(tok, childrule, tree, reslist, parent, &node)) { + case -1: + continue; + case 1: + /* no more found */ + x = 0; + fprintf(stderr, "No resource found @ %s\n", tok); + break; + case 0: + /* another is found */ + break; + } + if (!x) /* no more found */ + break; + + /* childrule = rule set of this child at this point */ + /* tok = set above; if we got this far, we're all set */ + /* Kaboom */ + + build_tree(&node->rn_child, node, childrule, rulelist, reslist, tok); + } + + //printf("ASCEND: %s / %s\n", rule?rule->rr_type:"(none)", base); + return 0; +} + +/** + Set up to call build_tree. Hides the nastiness from the user. + + @param tree Tree pointer. Should start as a pointer to NULL. + @param rulelist List of all rules allowed + @param reslist List of all currently defined resources + @return 0 + @see build_tree destroy_resource_tree + */ +int +build_resource_tree(resource_node_t ** tree, resource_rule_t ** rulelist, resource_t ** reslist) +{ + resource_node_t *root = NULL; + char tok[512]; + + snprintf(tok, sizeof(tok), "%s", RESOURCE_TREE_ROOT); + + /* Find and build the list of root nodes */ + build_tree(&root, NULL, NULL /*curr */ , rulelist, reslist, tok); + + if (root) + *tree = root; + + return 0; +} + +/** + Deconstruct a resource tree. + + @param tree Tree to obliterate. + @see build_resource_tree + */ +void +destroy_resource_tree(resource_node_t ** tree) +{ + resource_node_t *node; + + while ((node = *tree)) { + if ((*tree)->rn_child) + destroy_resource_tree(&(*tree)->rn_child); + + list_remove(tree, node); + + if (node->rn_actions) { + free(node->rn_actions); + } + free(node); + } +} + +static inline int +_do_child_levels(xmlNode ** xpp, xmlNode * rmp, resource_node_t ** tree, resource_t * first) +{ + resource_node_t *node = *tree; + resource_t *res = node->rn_resource; + resource_rule_t *rule = res->r_rule; + int l, lev, x, rv = 0; + + for (l = 1; l <= RESOURCE_MAX_LEVELS; l++) { + + for (x = 0; rule->rr_childtypes && rule->rr_childtypes[x].rc_name; x++) { + + lev = rule->rr_childtypes[x].rc_startlevel; + + if (!lev || lev != l) + continue; + + /* Do op on all children at our level */ + rv |= _res_op(xpp, rmp, &node->rn_child, first, rule->rr_childtypes[x].rc_name); + + if (rv & SFL_FAILURE) + return rv; + } + + if (rv != 0) + return rv; + } + + return rv; +} + +static inline int +_xx_child_internal(xmlNode ** xpp, xmlNode * rmp, resource_node_t * node, resource_t * first, + resource_node_t * child) +{ + int x; + resource_rule_t *rule = node->rn_resource->r_rule; + + for (x = 0; rule->rr_childtypes && rule->rr_childtypes[x].rc_name; x++) { + if (!strcmp(child->rn_resource->r_rule->rr_type, rule->rr_childtypes[x].rc_name)) { + if (rule->rr_childtypes[x].rc_startlevel || rule->rr_childtypes[x].rc_stoplevel) { + return 0; + } + } + } + + return _res_op_internal(xpp, rmp, &child, first, child->rn_resource->r_rule->rr_type, child); +} + +static inline int +_do_child_default_level(xmlNode ** xpp, xmlNode * rmp, resource_node_t ** tree, resource_t * first) +{ + resource_node_t *node = *tree, *child; + int y, rv = 0; + + list_for(&node->rn_child, child, y) { + rv |= _xx_child_internal(xpp, rmp, node, first, child); + + if (rv & SFL_FAILURE) + return rv; + } + + return rv; +} + +/** + Nasty codependent function. Perform an operation by numerical level + at some point in the tree. This allows indirectly-dependent resources + (such as IP addresses and user scripts) to have ordering without requiring + a direct dependency. + + @param tree Resource tree to search/perform operations on + @param first Resource we're looking to perform the operation on, + if one exists. + @param ret Unused, but will be used to store status information + such as resources consumed, etc, in the future. + @param op Operation to perform if either first is found, + or no first is declared (in which case, all nodes + in the subtree). + @see _res_op res_exec + */ +static int +_res_op_by_level(xmlNode ** xpp, xmlNode * rmp, resource_node_t ** tree, resource_t * first) +{ + resource_node_t *node = *tree; + resource_t *res = node->rn_resource; + resource_rule_t *rule = res->r_rule; + int rv = 0; + + if (!rule->rr_childtypes) + return _res_op(xpp, rmp, &node->rn_child, first, NULL); + + rv |= _do_child_levels(xpp, rmp, tree, first); + if (rv & SFL_FAILURE) + return rv; + + /* default level after specified ones */ + rv |= _do_child_default_level(xpp, rmp, tree, first); + + return rv; +} + +/** + Nasty codependent function. Perform an operation by type for all siblings + at some point in the tree. This allows indirectly-dependent resources + (such as IP addresses and user scripts) to have ordering without requiring + a direct dependency. + + @param tree Resource tree to search/perform operations on + @param first Resource we're looking to perform the operation on, + if one exists. + @param type Type to look for. + @see _res_op_by_level res_exec + */ +static inline int +_res_op_internal(xmlNode ** xpp, xmlNode * rmp, + resource_node_t __attribute__ ((unused)) ** tree, + resource_t * first, char *type, resource_node_t * node) +{ + int rv = 0, me; + + /* Restore default operation. */ + + /* If we're starting by type, do that funky thing. */ + if (type && strlen(type) && strcmp(node->rn_resource->r_rule->rr_type, type)) + return 0; + + /* If the resource is found, all nodes in the subtree must + have the operation performed as well. */ + me = !first || (node->rn_resource == first); + + /* Start starts before children */ + if (me) { + + rv = res_do_flatten(xpp, rmp, node, NULL, 0); + + } + + if (node->rn_child) { + rv |= _res_op_by_level(xpp, rmp, &node, me ? NULL : first); + } + + return rv; +} + +/** + Nasty codependent function. Perform an operation by type for all siblings + at some point in the tree. This allows indirectly-dependent resources + (such as IP addresses and user scripts) to have ordering without requiring + a direct dependency. + + @param tree Resource tree to search/perform operations on + @param first Resource we're looking to perform the operation on, + if one exists. + @param type Type to look for. + @see _res_op_by_level res_exec + */ +int +_res_op(xmlNode ** xpp, xmlNode * rmp, resource_node_t ** tree, resource_t * first, char *type) +{ + resource_node_t *node; + int count = 0, rv = 0; + + list_for(tree, node, count) { + rv |= _res_op_internal(xpp, rmp, tree, first, type, node); + + if (rv & SFL_FAILURE) + return rv; + } + + return rv; +} + +/** + Flatten resources for a service and return the pointer to it. + + @param tree Tree to search for our resource. + @param res Resource to start/stop + @param ret Unused + */ +int +res_flatten(xmlNode ** xpp, xmlNode * rmp, resource_node_t ** tree, resource_t * res) +{ + return _res_op(xpp, rmp, tree, res, NULL); +} diff --git a/__root__/ccs-flatten/xmlconf.c b/__root__/ccs-flatten/xmlconf.c new file mode 100644 index 0000000..92a7245 --- /dev/null +++ b/__root__/ccs-flatten/xmlconf.c @@ -0,0 +1,139 @@ +/* + Copyright Red Hat, Inc. 2004 + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ +#include <stdio.h> +#include <assert.h> +#include <libxml/xmlmemory.h> +#include <libxml/parser.h> +#include <libxml/xpath.h> +#include <string.h> +#include <xmlconf.h> + +static xmlDocPtr conf_doc = NULL; +static const char *conffile = "/etc/cluster/cluster.conf"; + +/** + Execute an XPath query, returning the first match. Multiple matches are + ignored. Please be advised that this is quite inefficient. + + @param doc Loaded XML document to search + @param ctx Predefined XML XPath context + @param query Query to execute. + @return newly allocated pointer to value or NULL if not found. + */ +char * +xpath_get_one(xmlDocPtr __attribute__ ((unused)) doc, xmlXPathContextPtr ctx, char *query) +{ + char *val = NULL, *ret = NULL; + xmlXPathObjectPtr obj; + xmlNodePtr node; + size_t size = 0; + int nnv = 0; + + obj = xmlXPathEvalExpression((unsigned char *)query, ctx); + if (!obj) + return NULL; + if (!obj->nodesetval) + goto out; + if (obj->nodesetval->nodeNr <= 0) + goto out; + + node = obj->nodesetval->nodeTab[0]; + if (!node) + goto out; + + if (((node->type == XML_ATTRIBUTE_NODE) && strstr(query, "@*")) || + ((node->type == XML_ELEMENT_NODE) && strstr(query, "child::*"))) { + if (node->children && node->children->content) + size = strlen((char *)node->children->content) + strlen((char *)node->name) + 2; + else + size = strlen((char *)node->name) + 2; + nnv = 1; + } else { + if (node->children && node->children->content) { + size = strlen((char *)node->children->content) + 1; + } else { + goto out; + } + } + + val = (char *)malloc(size); + if (!val) + goto out; + memset(val, 0, size); + if (nnv) { + sprintf(val, "%s=%s", node->name, (node->children && node->children->content) ? + (char *)node->children->content : ""); + } else { + sprintf(val, "%s", (node->children && node->children->content) ? node->children->content : + node->name); + } + + ret = val; + out: + xmlXPathFreeObject(obj); + + return ret; +} + +int +conf_open(void) +{ + xmlInitParser(); + conf_doc = xmlParseFile(conffile); + if (!conf_doc) + return -1; + return 0; +} + +xmlDocPtr +conf_get_doc(void) +{ + return conf_doc; +} + +int +conf_close(void) +{ + xmlFreeDoc(conf_doc); + conf_doc = NULL; + return 0; +} + +void +conf_setconfig(char *path) +{ + conffile = path; +} + +int +conf_get(char *path, char **value) +{ + char *foo; + xmlXPathContextPtr ctx; + + ctx = xmlXPathNewContext(conf_doc); + foo = xpath_get_one(conf_doc, ctx, path); + xmlXPathFreeContext(ctx); + + if (foo) { + *value = foo; + return 0; + } + return 1; +} diff --git a/__root__/ccs-flatten/xmlconf.h b/__root__/ccs-flatten/xmlconf.h new file mode 100644 index 0000000..5df7ce0 --- /dev/null +++ b/__root__/ccs-flatten/xmlconf.h @@ -0,0 +1,11 @@ +#ifndef _RG_LOCKS_H +# define _RG_LOCKS_H + +int conf_open(void); +int conf_close(void); +void conf_setconfig(char *path); +int conf_get(char *path, char **value); +xmlDocPtr conf_get_doc(void); +char *xpath_get_one(xmlDocPtr doc, xmlXPathContextPtr ctx, char *query); + +#endif |