Clean up RPM spec for newer distributions
[clumanager.git] / librhcm / xmlconfig.c
blobedbdfed47f65741dd240880fc8e8ba9e037c7894
1 /*
2 Copyright Red Hat, Inc. 2002-2003
4 The Red Hat Cluster Manager API Library is free software; you can
5 redistribute it and/or modify it under the terms of the GNU Lesser
6 General Public License as published by the Free Software Foundation;
7 either version 2.1 of the License, or (at your option) any later
8 version.
10 The Red Hat Cluster Manager API Library is distributed in the hope
11 that it will be useful, but WITHOUT ANY WARRANTY; without even the
12 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13 PURPOSE. See the GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18 USA.
20 /** @file
21 * Configuration Library using libxml2 Doc Tree.
23 * XXX Needs Doxygenification.
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <libxml/xmlmemory.h>
30 #include <libxml/parser.h>
31 #include <sys/queue.h>
32 #include <signal.h>
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <regex.h> /* Regular Expression Matching */
36 #include <fnmatch.h> /* Glob pattern matching */
37 #include <xmlconfig.h>
38 #include <fcntl.h>
39 #include <errno.h>
42 struct _uint_stack {
43 struct _uint_stack *uis_next;
44 unsigned int uis_val;
48 static inline int
49 _uis_push(struct _uint_stack **stack, int val)
51 struct _uint_stack *new;
53 if (!stack)
54 return -1;
56 if (!(new = malloc(sizeof(*new))))
57 return -1;
59 new->uis_next = *stack;
60 new->uis_val = val;
61 *stack = new;
63 return 0;
67 static inline int
68 _uis_pop(struct _uint_stack **stack)
70 struct _uint_stack *del;
71 int rv;
73 if (!*stack)
74 return -1;
76 del = *stack;
77 *stack = (*stack)->uis_next;
78 rv = del->uis_val;
79 free(del);
81 return rv;
85 static inline void
86 _uis_kill(struct _uint_stack **stack)
88 while (_uis_pop(stack) != -1);
92 static inline xmlAttrPtr
93 _xmlGetAttrPtr(xmlNodePtr cur, const char *prop)
95 xmlAttrPtr attr = cur->properties;
97 for (; attr; attr = attr->next)
98 if (!strcasecmp((char *)attr->name, prop))
99 return attr;
101 return NULL;
105 static inline int
106 _is_regex(const char *token)
108 return (strcspn(token,"([.*|+])") != strlen(token));
113 * construct a list of all tokens in our XML tree in the form:
114 * "tag%subtag%subtag1%abc123%property"
115 * TODO optimize, optimize, optimize...
118 xtree_tl_build(xmlDocPtr xtree, struct _token_list_head *head,
119 const char *m_pat, int m_type)
121 xmlNodePtr cur = xmlDocGetRootElement(xtree);
122 xmlAttrPtr attr = NULL;
123 char token[MAX_TOKEN_LEN], *prop;
124 unsigned int pos = 0, tmp;
125 struct _uint_stack *stack = NULL;
126 struct _token_list_node *tnode;
127 int count = 0;
129 MATCH_INIT(m_pat, m_type);
131 if (!head || !cur) {
132 MATCH_FREE(m_pat, m_type);
133 return -1;
136 TAILQ_INIT(head);
138 cur = cur->xmlChildrenNode;
139 while (cur) {
140 if (cur->type == XML_TEXT_NODE)
141 goto next;
143 strncpy(token + pos, (char *)cur->name, sizeof(token) - pos);
144 prop = (char *)xmlGetProp(cur, (xmlChar *)"id");
145 if (prop) {
146 strncat(token + strlen(token), prop,
147 MIN(strlen(prop) + 1,
148 sizeof(token) - strlen(token)));
149 xmlFree(prop);
152 if (cur->xmlChildrenNode) {
153 _uis_push(&stack, pos);
154 cur = cur->xmlChildrenNode;
155 strncat(token + strlen(token), "%",
156 MIN(2, sizeof(token) - strlen(token)));
157 pos = strlen(token);
158 continue;
161 /* Here we tack on the properties */
162 tmp = strlen(token);
163 strncat(token + tmp, "%", MIN(2, sizeof(token) - tmp));
164 nochild:
165 tmp = strlen(token);
166 attr = cur->properties;
167 while (attr) {
168 if (!strcasecmp((char *)attr->name,"id")) {
169 attr = attr->next;
170 continue;
173 strncpy(token + tmp, (char *)attr->name,
174 MIN(strlen((char *)attr->name) + 1,
175 sizeof(token) - tmp));
177 if (!MATCH(token, m_pat, m_type)) {
178 attr = attr->next;
179 continue;
182 if (!(tnode = malloc(sizeof(*tnode)))) {
183 perror("malloc");
184 MATCH_FREE(m_pat, m_type);
185 _uis_kill(&stack);
186 return -1;
189 memset(tnode,0,sizeof(*tnode));
191 if (!(tnode->tl_token = strdup(token))) {
192 perror("strdup");
193 MATCH_FREE(m_pat, m_type);
194 _uis_kill(&stack);
195 free(tnode);
196 return -1;
199 if (!(tnode->tl_value =
200 strdup((char *)attr->children->content))) {
201 perror("strdup");
202 MATCH_FREE(m_pat, m_type);
203 _uis_kill(&stack);
204 free(tnode->tl_token);
205 free(tnode);
206 return -1;
209 tnode->tl_terminal = attr;
211 TAILQ_INSERT_TAIL(head, tnode, tl_chain);
212 count++;
213 attr = attr->next;
215 token[tmp] = 0;
217 next:
218 if (cur->next) {
219 cur = cur->next;
220 continue;
223 if (pos) {
224 token[pos] = 0;
225 pos = _uis_pop(&stack);
226 cur = cur->parent;
227 goto nochild;
230 break;
233 _uis_kill(&stack);
234 MATCH_FREE(m_pat,m_type);
235 return count;
239 void
240 xtree_tl_free(struct _token_list_head *head)
242 struct _token_list_node *cur, *back;
244 cur = head->tqh_first;
245 while (cur) {
246 back = cur;
247 cur = cur->tl_chain.tqe_next;
249 TAILQ_REMOVE(head, back, tl_chain);
250 free(back->tl_token);
251 free(back->tl_value);
252 free(back);
255 head->tqh_first = NULL;
259 static char *
260 _extract_int(char *chunk, char *rv, int rv_size)
262 int id_loc;
263 char *id;
265 if ((id_loc = strcspn(chunk,"0123456789")) < strlen(chunk)) {
266 id = chunk + id_loc;
267 strncpy(rv, id, rv_size);
268 *id = 0;
269 return rv;
272 return NULL;
276 static int
277 _has_attributes(xmlNodePtr node)
279 xmlAttrPtr attr = node->properties;
281 while (attr) {
282 if ((attr->type == XML_ATTRIBUTE_NODE) &&
283 strcasecmp((char *)attr->name,"id"))
284 return 1;
285 attr = attr->next;
288 return 0;
292 static int
293 _has_children(xmlNodePtr node)
295 xmlNodePtr cur = node->xmlChildrenNode;
297 while (cur) {
298 if (node->type == XML_ELEMENT_NODE)
299 return 1;
300 cur = cur->next;
303 return 0;
308 _xtree_del(xmlAttrPtr attr)
310 xmlNodePtr cur, pp;
312 if (!attr)
313 return 0;
315 cur = attr->parent;
316 xmlRemoveProp(attr);
318 /* Ok, check if that was our last property/child */
319 while (!_has_children(cur) && !_has_attributes(cur)) {
320 pp = cur->parent;
321 xmlUnlinkNode(cur);
322 xmlFreeNode(cur);
323 cur = pp;
326 return 0;
330 xmlAttrPtr
331 _xtree_find(xmlDocPtr xtree, const char *token)
333 char *tok = NULL,
334 *part = NULL,
335 *nextpart = NULL,
336 *id = NULL,
337 *id_check = NULL,
338 buf[16];
339 xmlNodePtr cur = xmlDocGetRootElement(xtree),
340 last_parent = NULL;
341 xmlAttrPtr rv = NULL;
343 /* duplicate our inbound token, so we can tokenize it */
344 if (!cur || (!(tok = malloc(strlen(token)+1))))
345 return NULL;
347 /* Set up */
348 strcpy(tok,token);
349 last_parent = cur;
350 cur = cur->xmlChildrenNode;
351 part = tok;
353 while ((nextpart = strchr(part,'%')) && cur) {
354 *nextpart = 0; /* remove '%' */
355 nextpart++;
356 id = _extract_int(part, buf, sizeof(buf));
358 while (cur) { /* check for drop-to-child */
359 if (xmlStrcmp(cur->name, (xmlChar *)part) != 0) {
360 cur = cur->next;
361 continue;
364 if (id) {
365 /* compare the ID */
366 id_check = (char *)xmlGetProp(cur,
367 (xmlChar *)"id");
369 if (id_check && strcasecmp(id, id_check)) {
370 cur = cur->next;
371 xmlFree(id_check);
372 continue;
375 if (id_check)
376 xmlFree(id_check);
377 id = NULL;
380 last_parent = cur;
381 cur = cur->xmlChildrenNode;
382 break;
386 * if id is till set here, we have an id from above, but it
387 * wasn't found.
389 if (id)
390 break;
392 part = nextpart;
395 /* clean up, etc. */
396 if (!nextpart) {
397 if (id) /* ID not found! */
398 rv = NULL;
399 else /* xml node property */
400 rv = _xmlGetAttrPtr(last_parent, part);
403 free(tok);
405 return rv;
410 * bottom-up deletion... ;)
413 xtree_del(xmlDocPtr xtree, const char *token)
415 return _xtree_del(_xtree_find(xtree,token));
420 xtree_set(xmlDocPtr xtree, const char *token, char *value)
422 char *tok = NULL,
423 *part = NULL,
424 *nextpart = NULL,
425 *id_check = NULL,
426 *id = NULL,
427 child = 0,
428 buf[16],
430 xmlNodePtr cur = xmlDocGetRootElement(xtree),
431 last_parent = NULL,
432 newnode = NULL;
434 /* Reject regexps in this case */
435 if (_is_regex(token))
436 return -1;
438 /* duplicate our inbound token, so we can hack it up */
439 if (!cur || !strlen(token) || (!(tok = malloc(strlen(token)+1))))
440 return -1;
442 /* Set up */
443 strncpy(tok, token, strlen(token)+1);
444 last_parent = cur;
445 cur = cur->xmlChildrenNode;
446 part = tok;
448 nextpart = strchr(part, '%');
449 while (nextpart) {
450 *nextpart = 0; /* remove '%' */
451 nextpart++;
453 id = _extract_int(part, buf, sizeof(buf));
455 while (cur) { /* check for drop-to-child */
457 if (strcasecmp((char *)cur->name, part)) {
458 cur = cur->next;
459 continue;
462 if (id) {
463 /* compare the ID */
464 id_check = (char *)xmlGetProp(cur,
465 (xmlChar *)"id");
467 if (strcasecmp(id, id_check)) {
468 /* not the same */
469 cur = cur->next;
470 xmlFree(id_check);
471 continue;
474 id = NULL;
475 xmlFree(id_check);
478 last_parent = cur;
479 cur = cur->xmlChildrenNode;
480 part = nextpart;
481 child = 1;
482 break;
486 * Ok, we dropped to child successfully. No IDs needed,
487 * just continue
489 if (child) {
490 child = 0;
491 nextpart = strchr(part, '%');
492 continue;
496 * ok, so we've run off the end of our token or the
497 * end of the XML tree.
499 if (!nextpart)
500 /* Attribute! */
501 break;
503 /* New regular node. Add it. */
504 sv = *nextpart;
505 *nextpart = 0;
506 newnode = xmlNewNode(NULL, (xmlChar *)part);
507 *nextpart = sv;
509 cur = xmlAddChild(last_parent, newnode);
510 if (!cur) {
511 xmlFreeNode(newnode);
512 free(tok);
513 return -1;
516 last_parent = cur;
517 cur = newnode->xmlChildrenNode; /* same as NULL */
519 /* Don't forget to continue eating the token */
520 part = nextpart;
521 nextpart = strchr(part, '%');
523 /* set id property if it exists */
524 if (id)
525 xmlSetProp(newnode, (xmlChar *)"id", (xmlChar *)id);
528 /* Write out the property */
529 if (!nextpart)
530 xmlSetProp(last_parent, (xmlChar *)part, (xmlChar *)value);
532 free(tok);
533 return 0;
538 xtree_get(xmlDocPtr xtree, const char *token, char *dflt, char **value)
540 xmlAttrPtr attr;
542 /* Reject regexps in this case */
543 if (_is_regex(token)) {
544 printf("token is regex\n");
545 return -1;
548 attr = _xtree_find(xtree, token);
550 if (attr && attr->children->content &&
551 strlen((char*)attr->children->content)) {
552 if (value)
553 *value = (char *)attr->children->content;
554 return 0;
557 if (dflt) {
558 if (value)
559 *value = dflt;
560 return 0;
563 return 1;
568 xtree_readfile(const char *filename, xmlDocPtr *xtreep)
570 xmlNodePtr cur;
572 xmlKeepBlanksDefault(0);
573 xmlIndentTreeOutput = 1;
575 *xtreep = xmlParseFile(filename);
577 if (!*xtreep)
578 return -1;
580 if (!((cur = xmlDocGetRootElement(*xtreep)))) {
581 xmlFreeDoc(*xtreep);
582 *xtreep = NULL;
583 return -1;
586 return 0;
591 xtree_readbuffer(const char *buffer, size_t size, xmlDocPtr *xtreep)
593 xmlNodePtr cur;
595 xmlKeepBlanksDefault(0);
596 xmlIndentTreeOutput = 1;
598 *xtreep = xmlParseMemory(buffer, size);
600 if (!*xtreep) {
601 printf("parse failure %p %d\n", buffer, (int)size);
602 return -1;
605 if (!((cur = xmlDocGetRootElement(*xtreep)))) {
606 printf("root element failure\n");
607 xmlFreeDoc(*xtreep);
608 *xtreep = NULL;
609 return -1;
612 return 0;
617 xtree_writefile(const char *filename, xmlDocPtr xtree)
619 char tmpfn[1024];
620 int fd, tmpfd;
621 xmlChar *buffer;
622 struct flock flock;
623 int n, remain, written, size = 0;
625 snprintf(tmpfn, sizeof(tmpfn), "%s.XXXXXX", filename);
626 tmpfd = mkstemp(tmpfn);
627 if (tmpfd == -1)
628 return -1;
630 memset(&flock, 0, sizeof(flock));
631 flock.l_type = F_WRLCK;
633 fd = open(filename, O_WRONLY | O_CREAT | O_SYNC);
634 if (fd == -1) {
635 n = errno;
636 close(tmpfd);
637 unlink(tmpfn);
638 errno = n;
639 return -1;
642 while (fcntl(fd, F_SETLKW, &flock) == -1) {
643 if (errno == EINTR)
644 continue;
645 n = errno;
646 close(fd);
647 close(tmpfd);
648 unlink(tmpfn);
649 errno = n;
650 return -1;
653 xmlDocDumpFormatMemory(xtree, (xmlChar **)&buffer, (int *)&size, 1);
655 written = 0;
656 remain = size;
657 while (remain) {
658 n = write(tmpfd, buffer + written, remain);
660 if (n == -1) {
661 if (errno == EINTR)
662 continue;
664 free(buffer);
665 n = errno;
666 close(fd);
667 close(tmpfd);
668 unlink(tmpfn);
669 errno = n;
670 return -1;
673 written += n;
674 remain -= n;
677 xmlFree(buffer);
678 if (rename(tmpfn, filename) == -1) {
679 n = errno;
680 close(fd);
681 close(tmpfd);
682 unlink(tmpfn);
683 errno = n;
684 return -1;
687 close(fd);
688 fsync(tmpfd);
689 close(tmpfd);
690 return 0;
695 xtree_writebuffer(xmlDocPtr xtree, char **buffer, size_t *size)
697 *size = 0;
698 xmlDocDumpFormatMemory(xtree, (xmlChar **)buffer, (int *)size, 1);
699 return 0;
703 void
704 tlist_dump(struct _token_list_head *lh)
706 struct _token_list_node *cur;
708 cur = lh->tqh_first;
709 while (cur) {
710 printf("%s => %s\n", cur->tl_token,
711 cur->tl_terminal->children->content);
712 cur = cur->tl_chain.tqe_next;