2 * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the University of California, Berkeley nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include "cfzy_list.h"
35 #include "cfzy_htable.h"
36 #include "cfzy_confnode.h"
37 #include "cfzy_conftree.h"
38 #include "cfzy_conftree_parser.h"
40 #define LOG(level, fmt, args...) \
41 CFZY_LOG("conftree", level, fmt, ##args)
43 /* Parse a configuration tree and add all the nodes in the
44 * hashtable. Return the number of nodes or -1 on error. */
45 static int conftree_fill_htable(struct cfzy_conftree *conftree,
46 struct cfzy_confnode *n)
48 struct cfzy_confnode *c;
51 if (n->name != NULL) {
52 if (cfzy_htable_lookup(conftree->nodes_htable, n->name) != NULL) {
53 LOG(ERR, "duplicate symbol <%s>\n", n->name);
57 if (cfzy_htable_add(conftree->nodes_htable, n->name, n) < 0)
60 count = 1; /* count the nodes recursively */
62 TAILQ_FOREACH(c, &n->children, child_next) {
63 ret = conftree_fill_htable(conftree, c);
72 /* Parse a configuration tree and add all the nodes in the hashtable */
73 static int conftree_generate_deplist(struct cfzy_confnode *n)
75 struct cfzy_confnode *c;
77 n->node_deps = cfzy_confnode_get_deplist(n);
78 if (n->node_deps == NULL)
81 TAILQ_FOREACH(c, &n->children, child_next) {
82 if (conftree_generate_deplist(c) < 0)
89 /* Parse a configuration tree and add all the nodes in the
90 * hashtable. Return the number of nodes in the prio_list. */
91 static int conftree_fill_prio_list(struct cfzy_conftree *conftree,
92 struct cfzy_confnode *n)
94 struct cfzy_list_elt *e;
95 struct cfzy_confnode *c;
99 if (n->in_prio_list == 1) {
103 /* if the node is not in prio_list, check if all
104 * prerequisites are in prio list, and add in the
106 TAILQ_FOREACH(e, n->node_deps, next) {
108 if (c->in_prio_list == 0) {
114 TAILQ_INSERT_TAIL(&conftree->prio_list, n, prio_next);
120 /* process children */
121 TAILQ_FOREACH(c, &n->children, child_next) {
122 ret = conftree_fill_prio_list(conftree, c);
131 /* create a new configuration tree from filename */
132 struct cfzy_conftree *cfzy_conftree_new(const char *filename)
134 struct cfzy_conftree *conftree;
135 int node_count, prio_count, prev;
137 LOG(INFO, "Open conftree <%s>\n", filename);
139 conftree = malloc(sizeof(*conftree));
140 if (conftree == NULL)
142 memset(conftree, 0, sizeof(*conftree));
144 conftree->path = strdup(filename);
145 if (conftree->path == NULL) {
146 cfzy_conftree_free(conftree);
150 conftree->root = cfzy_confnode_root_new(conftree);
151 if (conftree->root == NULL) {
152 cfzy_conftree_free(conftree);
156 /* parse the configuration tree file */
157 if (cfzy_conftree_parse(filename, conftree) < 0) {
158 LOG(ERR, "cannot parse configuration tree\n");
159 cfzy_conftree_free(conftree);
163 conftree->nodes_htable = cfzy_htable_alloc();
164 if (conftree->nodes_htable == NULL) {
165 LOG(ERR, "cannot allocate conftree htable\n");
166 cfzy_conftree_free(conftree);
170 /* fill a htable indexed by node name */
171 node_count = conftree_fill_htable(conftree, conftree->root);
172 if (node_count < 0) {
173 LOG(ERR, "cannot fill conftree htable\n");
174 cfzy_conftree_free(conftree);
177 conftree->count = node_count;
179 /* for each node, generate the list of prerequisite nodes */
180 if (conftree_generate_deplist(conftree->root) < 0) {
181 LOG(ERR, "cannot generate dep list\n");
182 cfzy_conftree_free(conftree);
186 /* generate a list ordered by node priority: if Node1 depends
187 * on Node2, then Node1 is located after Node2 in the list. */
188 TAILQ_INIT(&conftree->prio_list);
191 prio_count = conftree_fill_prio_list(conftree, conftree->root);
192 if (prio_count < 0) {
193 LOG(ERR, "cannot generate prio list\n");
194 cfzy_conftree_free(conftree);
197 if (prio_count == node_count)
199 if (prio_count == prev) {
200 LOG(ERR, "circular dependency in conf tree\n");
201 cfzy_conftree_free(conftree);
210 /* free a configuration tree */
211 void cfzy_conftree_free(struct cfzy_conftree *conftree)
213 if (conftree->path != NULL)
214 free(conftree->path);
215 if (conftree->nodes_htable != NULL)
216 cfzy_htable_free(conftree->nodes_htable, NULL);
217 if (conftree->root != NULL)
218 cfzy_confnode_free(conftree->root);
222 int cfzy_conftree_dump(const struct cfzy_conftree *conftree,
223 const char *filename)
227 f = fopen(filename, "w");
229 LOG(ERR, "Cannot open <%s>: %s\n", filename, strerror(errno));
233 if (cfzy_confnode_dump(conftree->root, f,
234 CFZY_DUMP_MODE_FULL_RECURSIVE) < 0) {
235 LOG(ERR, "Cannot dump confnode tree\n");
244 int cfzy_conftree_evaluate(struct cfzy_conftree *conftree)
246 struct cfzy_confnode *n;
248 TAILQ_FOREACH(n, &conftree->prio_list, prio_next) {
249 if (cfzy_confnode_evaluate(n) < 0) {
250 LOG(ERR, "Cannot evaluate conftree\n");
253 LOG(DEBUG, "eval %s -> %s\n", n->name, n->effective_value);
259 /* same than strcmp() but allow string to be NULL */
260 static int strcmp2(const char *s1, const char *s2)
262 if (s1 == NULL && s2 == NULL)
268 return strcmp(s1, s2);
271 static int __filter(struct cfzy_confnode *n, struct cfzy_list_head *node_list,
275 struct cfzy_confnode *c;
277 if ((flags & CFZY_FILTER_F_USR_UNSET) && n->user_value == NULL)
279 else if ((flags & CFZY_FILTER_F_EFF_UNSET) && n->effective_value == NULL)
281 else if ((flags & CFZY_FILTER_F_USR_CHANGED) &&
282 strcmp2(n->user_value, n->old_user_value))
284 else if ((flags & CFZY_FILTER_F_EFF_CHANGED) &&
285 strcmp2(n->effective_value, n->old_effective_value))
289 if (cfzy_list_add_tail(node_list, n) < 0) {
290 LOG(ERR, "cannot add node in list\n");
296 TAILQ_FOREACH(c, &n->children, child_next) {
297 if (__filter(c, node_list, flags) < 0)
304 /* Get a list of nodes matching conditions (for instance the nodes
305 * that have no user value) */
306 struct cfzy_list_head *
307 cfzy_conftree_filter(struct cfzy_conftree *conftree, int flags)
309 struct cfzy_list_head *node_list = NULL;
311 node_list = cfzy_list_alloc();
312 if (node_list == NULL) {
313 LOG(ERR, "cannot allocate node list\n");
317 if (__filter(conftree->root, node_list, flags) < 0) {
318 LOG(ERR, "cannot filter nodes\n");
319 cfzy_list_free(node_list, NULL);
326 static int __save_values(struct cfzy_confnode *n)
328 struct cfzy_confnode *c;
330 /* duplicate user value */
331 if (n->old_user_value != NULL) {
332 free(n->old_user_value);
333 n->old_user_value = NULL;
335 if (n->user_value != NULL) {
336 n->old_user_value = strdup(n->user_value);
337 if (n->old_user_value == NULL) {
338 LOG(ERR, "cannot duplicate value\n");
343 /* duplicate effective value */
344 if (n->old_effective_value != NULL) {
345 free(n->old_effective_value);
346 n->old_effective_value = NULL;
348 if (n->effective_value != NULL) {
349 n->old_effective_value = strdup(n->effective_value);
350 if (n->old_effective_value == NULL) {
351 LOG(ERR, "cannot duplicate value\n");
356 /* do the same on children */
357 TAILQ_FOREACH(c, &n->children, child_next) {
358 if (__save_values(c) < 0)
365 int cfzy_conftree_save_values(struct cfzy_conftree *conftree)
367 return __save_values(conftree->root);