]> mj.ucw.cz Git - libucw.git/blob - lib/conf-section.c
Fixed bug in signal handling.
[libucw.git] / lib / conf-section.c
1 /*
2  *      UCW Library -- Configuration files: sections
3  *
4  *      (c) 2001--2006 Robert Spalek <robert@ucw.cz>
5  *      (c) 2003--2006 Martin Mares <mj@ucw.cz>
6  *
7  *      This software may be freely distributed and used according to the terms
8  *      of the GNU Lesser General Public License.
9  */
10
11 #include "lib/lib.h"
12 #include "lib/conf.h"
13 #include "lib/conf-internal.h"
14 #include "lib/clists.h"
15 #include "lib/binsearch.h"
16
17 #include <string.h>
18
19 /* Dirty sections */
20
21 struct dirty_section {
22   struct cf_section *sec;
23   void *ptr;
24 };
25 #define GBUF_TYPE       struct dirty_section
26 #define GBUF_PREFIX(x)  dirtsec_##x
27 #include "lib/gbuf.h"
28 static dirtsec_t dirty;
29 static uns dirties;
30
31 void
32 cf_add_dirty(struct cf_section *sec, void *ptr)
33 {
34   dirtsec_grow(&dirty, dirties+1);
35   struct dirty_section *dest = dirty.ptr + dirties;
36   if (dirties && dest[-1].sec == sec && dest[-1].ptr == ptr)
37     return;
38   dest->sec = sec;
39   dest->ptr = ptr;
40   dirties++;
41 }
42
43 #define ASORT_PREFIX(x) dirtsec_##x
44 #define ASORT_KEY_TYPE  struct dirty_section
45 #define ASORT_ELT(i)    dirty.ptr[i]
46 #define ASORT_LT(x,y)   x.sec < y.sec || x.sec == y.sec && x.ptr < y.ptr
47 #include "lib/arraysort.h"
48
49 static void
50 sort_dirty(void)
51 {
52   if (dirties <= 1)
53     return;
54   dirtsec_sort(dirties);
55   // and compress the list
56   struct dirty_section *read = dirty.ptr + 1, *write = dirty.ptr + 1, *limit = dirty.ptr + dirties;
57   while (read < limit) {
58     if (read->sec != read[-1].sec || read->ptr != read[-1].ptr) {
59       if (read != write)
60         *write = *read;
61       write++;
62     }
63     read++;
64   }
65   dirties = write - dirty.ptr;
66 }
67
68 /* Initialization */
69
70 struct cf_section cf_sections;  // root section
71
72 struct cf_item *
73 cf_find_subitem(struct cf_section *sec, byte *name)
74 {
75   struct cf_item *ci = sec->cfg;
76   for (; ci->cls; ci++)
77     if (!strcasecmp(ci->name, name))
78       return ci;
79   return ci;
80 }
81
82 static void
83 inspect_section(struct cf_section *sec)
84 {
85   sec->flags = 0;
86   struct cf_item *ci;
87   for (ci=sec->cfg; ci->cls; ci++)
88     if (ci->cls == CC_SECTION) {
89       inspect_section(ci->u.sec);
90       sec->flags |= ci->u.sec->flags & (SEC_FLAG_DYNAMIC | SEC_FLAG_CANT_COPY);
91     } else if (ci->cls == CC_LIST) {
92       inspect_section(ci->u.sec);
93       sec->flags |= SEC_FLAG_DYNAMIC | SEC_FLAG_CANT_COPY;
94     } else if (ci->cls == CC_DYNAMIC || ci->cls == CC_BITMAP)
95       sec->flags |= SEC_FLAG_DYNAMIC;
96     else if (ci->cls == CC_PARSER) {
97       sec->flags |= SEC_FLAG_CANT_COPY;
98       if (ci->number < 0)
99         sec->flags |= SEC_FLAG_DYNAMIC;
100     }
101   if (sec->copy)
102     sec->flags &= ~SEC_FLAG_CANT_COPY;
103   sec->flags |= ci - sec->cfg;          // record the number of entries
104 }
105
106 void
107 cf_declare_section(byte *name, struct cf_section *sec, uns allow_unknown)
108 {
109   if (!cf_sections.cfg)
110   {
111     cf_sections.size = 50;
112     cf_sections.cfg = xmalloc_zero(cf_sections.size * sizeof(struct cf_item));
113   }
114   struct cf_item *ci = cf_find_subitem(&cf_sections, name);
115   if (ci->cls)
116     die("Cannot register section %s twice", name);
117   ci->cls = CC_SECTION;
118   ci->name = name;
119   ci->number = 1;
120   ci->ptr = NULL;
121   ci->u.sec = sec;
122   inspect_section(sec);
123   if (allow_unknown)
124     sec->flags |= SEC_FLAG_UNKNOWN;
125   ci++;
126   if (ci - cf_sections.cfg >= (int) cf_sections.size)
127   {
128     cf_sections.cfg = xrealloc(cf_sections.cfg, 2*cf_sections.size * sizeof(struct cf_item));
129     bzero(cf_sections.cfg + cf_sections.size, cf_sections.size * sizeof(struct cf_item));
130     cf_sections.size *= 2;
131   }
132 }
133
134 void
135 cf_init_section(byte *name, struct cf_section *sec, void *ptr, uns do_bzero)
136 {
137   if (do_bzero) {
138     ASSERT(sec->size);
139     bzero(ptr, sec->size);
140   }
141   for (struct cf_item *ci=sec->cfg; ci->cls; ci++)
142     if (ci->cls == CC_SECTION)
143       cf_init_section(ci->name, ci->u.sec, ptr + (addr_int_t) ci->ptr, 0);
144     else if (ci->cls == CC_LIST)
145       clist_init(ptr + (addr_int_t) ci->ptr);
146     else if (ci->cls == CC_DYNAMIC) {
147       void **dyn = ptr + (addr_int_t) ci->ptr;
148       if (!*dyn) {                      // replace NULL by an empty array
149         static uns zero = 0;
150         *dyn = (&zero) + 1;
151       }
152     }
153   if (sec->init) {
154     byte *msg = sec->init(ptr);
155     if (msg)
156       die("Cannot initialize section %s: %s", name, msg);
157   }
158 }
159
160 static byte *
161 commit_section(struct cf_section *sec, void *ptr, uns commit_all)
162 {
163   byte *err;
164   for (struct cf_item *ci=sec->cfg; ci->cls; ci++)
165     if (ci->cls == CC_SECTION) {
166       if ((err = commit_section(ci->u.sec, ptr + (addr_int_t) ci->ptr, commit_all))) {
167         log(L_ERROR, "Cannot commit section %s: %s", ci->name, err);
168         return "commit of a subsection failed";
169       }
170     } else if (ci->cls == CC_LIST) {
171       uns idx = 0;
172       CLIST_FOR_EACH(cnode *, n, * (clist*) (ptr + (addr_int_t) ci->ptr))
173         if (idx++, err = commit_section(ci->u.sec, n, commit_all)) {
174           log(L_ERROR, "Cannot commit node #%d of list %s: %s", idx, ci->name, err);
175           return "commit of a list failed";
176         }
177     }
178   if (sec->commit) {
179     /* We have to process the whole tree of sections even if just a few changes
180      * have been made, because there are dependencies between commit-hooks and
181      * hence we need to call them in a fixed order.  */
182 #define ARY_LT_X(ary,i,x) ary[i].sec < x.sec || ary[i].sec == x.sec && ary[i].ptr < x.ptr
183     struct dirty_section comp = { sec, ptr };
184     uns pos = BIN_SEARCH_FIRST_GE_CMP(dirty.ptr, dirties, comp, ARY_LT_X);
185
186     if (commit_all
187         || (pos < dirties && dirty.ptr[pos].sec == sec && dirty.ptr[pos].ptr == ptr))
188       return sec->commit(ptr);
189   }
190   return 0;
191 }
192
193 int
194 cf_commit_all(enum cf_commit_mode cm)
195 {
196   sort_dirty();
197   if (cm == CF_NO_COMMIT)
198     return 0;
199   if (commit_section(&cf_sections, NULL, cm == CF_COMMIT_ALL))
200     return 1;
201   dirties = 0;
202   return 0;
203 }