2 * UCW Library -- Configuration files: sections
4 * (c) 2001--2006 Robert Spalek <robert@ucw.cz>
5 * (c) 2003--2006 Martin Mares <mj@ucw.cz>
7 * This software may be freely distributed and used according to the terms
8 * of the GNU Lesser General Public License.
13 #include "lib/conf-internal.h"
14 #include "lib/clists.h"
15 #include "lib/binsearch.h"
19 #define TRY(f) do { byte *_msg = f; if (_msg) return _msg; } while (0)
23 struct dirty_section {
24 struct cf_section *sec;
27 #define GBUF_TYPE struct dirty_section
28 #define GBUF_PREFIX(x) dirtsec_##x
30 static dirtsec_t dirty;
34 cf_add_dirty(struct cf_section *sec, void *ptr)
36 dirtsec_grow(&dirty, dirties+1);
37 struct dirty_section *dest = dirty.ptr + dirties;
38 if (dirties && dest[-1].sec == sec && dest[-1].ptr == ptr)
45 #define ASORT_PREFIX(x) dirtsec_##x
46 #define ASORT_KEY_TYPE struct dirty_section
47 #define ASORT_ELT(i) dirty.ptr[i]
48 #define ASORT_LT(x,y) x.sec < y.sec || x.sec == y.sec && x.ptr < y.ptr
49 #include "lib/arraysort.h"
56 dirtsec_sort(dirties);
57 // and compress the list
58 struct dirty_section *read = dirty.ptr + 1, *write = dirty.ptr + 1, *limit = dirty.ptr + dirties;
59 while (read < limit) {
60 if (read->sec != read[-1].sec || read->ptr != read[-1].ptr) {
67 dirties = write - dirty.ptr;
72 struct cf_section cf_sections; // root section
75 cf_find_subitem(struct cf_section *sec, byte *name)
77 struct cf_item *ci = sec->cfg;
79 if (!strcasecmp(ci->name, name))
85 inspect_section(struct cf_section *sec)
89 for (ci=sec->cfg; ci->cls; ci++)
90 if (ci->cls == CC_SECTION) {
91 inspect_section(ci->u.sec);
92 sec->flags |= ci->u.sec->flags & (SEC_FLAG_DYNAMIC | SEC_FLAG_CANT_COPY);
93 } else if (ci->cls == CC_LIST) {
94 inspect_section(ci->u.sec);
95 sec->flags |= SEC_FLAG_DYNAMIC | SEC_FLAG_CANT_COPY;
96 } else if (ci->cls == CC_DYNAMIC)
97 sec->flags |= SEC_FLAG_DYNAMIC;
98 else if (ci->cls == CC_PARSER) {
99 sec->flags |= SEC_FLAG_CANT_COPY;
101 sec->flags |= SEC_FLAG_DYNAMIC;
104 sec->flags &= ~SEC_FLAG_CANT_COPY;
105 sec->flags |= ci - sec->cfg; // record the number of entries
109 cf_declare_section(byte *name, struct cf_section *sec, uns allow_unknown)
111 if (!cf_sections.cfg)
113 cf_sections.size = 50;
114 cf_sections.cfg = xmalloc_zero(cf_sections.size * sizeof(struct cf_item));
116 struct cf_item *ci = cf_find_subitem(&cf_sections, name);
118 die("Cannot register section %s twice", name);
119 ci->cls = CC_SECTION;
124 inspect_section(sec);
126 sec->flags |= SEC_FLAG_UNKNOWN;
128 if (ci - cf_sections.cfg >= (int) cf_sections.size)
130 cf_sections.cfg = xrealloc(cf_sections.cfg, 2*cf_sections.size * sizeof(struct cf_item));
131 bzero(cf_sections.cfg + cf_sections.size, cf_sections.size * sizeof(struct cf_item));
132 cf_sections.size *= 2;
137 cf_init_section(byte *name, struct cf_section *sec, void *ptr, uns do_bzero)
141 bzero(ptr, sec->size);
143 for (uns i=0; sec->cfg[i].cls; i++)
144 if (sec->cfg[i].cls == CC_SECTION)
145 cf_init_section(sec->cfg[i].name, sec->cfg[i].u.sec, ptr + (addr_int_t) sec->cfg[i].ptr, 0);
146 else if (sec->cfg[i].cls == CC_LIST)
147 clist_init(ptr + (addr_int_t) sec->cfg[i].ptr);
149 byte *msg = sec->init(ptr);
151 die("Cannot initialize section %s: %s", name, msg);
156 replace_null_dary(struct cf_item *item, void **ptr)
161 uns size = cf_type_size(item->type, item->u.utype);
162 cf_journal_block(ptr, size);
163 if (size <= sizeof(zero))
166 *ptr = cf_malloc_zero(size) + size;
170 commit_section(struct cf_section *sec, void *ptr, uns commit_all)
174 for (ci=sec->cfg; ci->cls; ci++)
175 if (ci->cls == CC_SECTION) {
176 if ((err = commit_section(ci->u.sec, ptr + (addr_int_t) ci->ptr, commit_all))) {
177 log(L_ERROR, "Cannot commit section %s: %s", ci->name, err);
178 return "commit of a subsection failed";
180 } else if (ci->cls == CC_LIST) {
182 CLIST_FOR_EACH(cnode *, n, * (clist*) (ptr + (addr_int_t) ci->ptr))
183 if (idx++, err = commit_section(ci->u.sec, n, commit_all)) {
184 log(L_ERROR, "Cannot commit node #%d of list %s: %s", idx, ci->name, err);
185 return "commit of a list failed";
189 /* We have to process the whole tree of sections even if just a few changes
190 * have been made, because there are dependencies between commit-hooks and
191 * hence we need to call them in a fixed order. */
192 #define ARY_LT_X(ary,i,x) ary[i].sec < x.sec || ary[i].sec == x.sec && ary[i].ptr < x.ptr
193 struct dirty_section comp = { sec, ptr };
194 uns pos = BIN_SEARCH_FIRST_GE_CMP(dirty.ptr, dirties, comp, ARY_LT_X);
197 || (pos < dirties && dirty.ptr[pos].sec == sec && dirty.ptr[pos].ptr == ptr))
198 TRY( sec->commit(ptr) );
200 for (ci=sec->cfg; ci->cls; ci++)
201 if (ci->cls == CC_DYNAMIC)
202 replace_null_dary(ci, ptr + (addr_int_t) ci->ptr);
207 cf_commit_all(enum cf_commit_mode cm)
210 if (cm == CF_NO_COMMIT)
212 if (commit_section(&cf_sections, NULL, cm == CF_COMMIT_ALL))