]> mj.ucw.cz Git - libucw.git/blob - ucw/conf-intr.c
libucw: Implemented Loging.Stream.FileDesc.
[libucw.git] / ucw / conf-intr.c
1 /*
2  *      UCW Library -- Configuration files: interpreter
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 "ucw/lib.h"
12 #include "ucw/conf.h"
13 #include "ucw/getopt.h"
14 #include "ucw/conf-internal.h"
15 #include "ucw/clists.h"
16
17 #include <string.h>
18 #include <stdio.h>
19
20 #define TRY(f)  do { char *_msg = f; if (_msg) return _msg; } while (0)
21
22 /* Register size of and parser for each basic type */
23
24 static char *
25 cf_parse_string(char *str, char **ptr)
26 {
27   *ptr = cf_strdup(str);
28   return NULL;
29 }
30
31 typedef char *cf_basic_parser(char *str, void *ptr);
32 static struct {
33   uns size;
34   void *parser;
35 } parsers[] = {
36   { sizeof(int), cf_parse_int },
37   { sizeof(u64), cf_parse_u64 },
38   { sizeof(double), cf_parse_double },
39   { sizeof(u32), cf_parse_ip },
40   { sizeof(char*), cf_parse_string },
41   { sizeof(int), NULL },                        // lookups are parsed extra
42   { 0, NULL },                                  // user-defined types are parsed extra
43 };
44
45 inline uns
46 cf_type_size(enum cf_type type, struct cf_user_type *utype)
47 {
48   if (type < CT_USER)
49     return parsers[type].size;
50   else
51     return utype->size;
52 }
53
54 static char *
55 cf_parse_lookup(char *str, int *ptr, const char * const *t)
56 {
57   const char * const *n = t;
58   uns total_len = 0;
59   while (*n && strcasecmp(*n, str)) {
60     total_len += strlen(*n) + 2;
61     n++;
62   }
63   if (*n) {
64     *ptr = n - t;
65     return NULL;
66   }
67   char *err = cf_malloc(total_len + strlen(str) + 60), *c = err;
68   c += sprintf(err, "Invalid value %s, possible values are: ", str);
69   for (n=t; *n; n++)
70     c+= sprintf(c, "%s, ", *n);
71   if (*t)
72     c[-2] = 0;
73   *ptr = -1;
74   return err;
75 }
76
77 static char *
78 cf_parse_ary(uns number, char **pars, void *ptr, enum cf_type type, union cf_union *u)
79 {
80   for (uns i=0; i<number; i++)
81   {
82     char *msg;
83     uns size = cf_type_size(type, u->utype);
84     if (type < CT_LOOKUP)
85       msg = ((cf_basic_parser*) parsers[type].parser) (pars[i], ptr + i * size);
86     else if (type == CT_LOOKUP)
87       msg = cf_parse_lookup(pars[i], ptr + i * size, u->lookup);
88     else if (type == CT_USER)
89       msg = u->utype->parser(pars[i], ptr + i * size);
90     else
91       ASSERT(0);
92     if (msg)
93       return number > 1 ? cf_printf("Item %d: %s", i+1, msg) : msg;
94   }
95   return NULL;
96 }
97
98 /* Interpreter */
99
100 #define T(x) #x,
101 char *cf_op_names[] = { CF_OPERATIONS };
102 #undef T
103 char *cf_type_names[] = { "int", "u64", "double", "ip", "string", "lookup", "user" };
104
105 #define DARY_HDR_SIZE ALIGN_TO(sizeof(uns), CPU_STRUCT_ALIGN)
106
107 static char *
108 interpret_set_dynamic(struct cf_item *item, int number, char **pars, void **ptr)
109 {
110   enum cf_type type = item->type;
111   cf_journal_block(ptr, sizeof(void*));
112   // boundary checks done by the caller
113   uns size = cf_type_size(item->type, item->u.utype);
114   *ptr = cf_malloc(DARY_HDR_SIZE + number * size) + DARY_HDR_SIZE;
115   DARY_LEN(*ptr) = number;
116   return cf_parse_ary(number, pars, *ptr, type, &item->u);
117 }
118
119 static char *
120 interpret_add_dynamic(struct cf_item *item, int number, char **pars, int *processed, void **ptr, enum cf_operation op)
121 {
122   enum cf_type type = item->type;
123   void *old_p = *ptr;
124   uns size = cf_type_size(item->type, item->u.utype);
125   ASSERT(size >= sizeof(uns));
126   int old_nr = old_p ? DARY_LEN(old_p) : 0;
127   int taken = MIN(number, ABS(item->number)-old_nr);
128   *processed = taken;
129   // stretch the dynamic array
130   void *new_p = cf_malloc(DARY_HDR_SIZE + (old_nr + taken) * size) + DARY_HDR_SIZE;
131   DARY_LEN(new_p) = old_nr + taken;
132   cf_journal_block(ptr, sizeof(void*));
133   *ptr = new_p;
134   if (op == OP_APPEND) {
135     memcpy(new_p, old_p, old_nr * size);
136     return cf_parse_ary(taken, pars, new_p + old_nr * size, type, &item->u);
137   } else if (op == OP_PREPEND) {
138     memcpy(new_p + taken * size, old_p, old_nr * size);
139     return cf_parse_ary(taken, pars, new_p, type, &item->u);
140   } else
141     return cf_printf("Dynamic arrays do not support operation %s", cf_op_names[op]);
142 }
143
144 static char *interpret_set_item(struct cf_item *item, int number, char **pars, int *processed, void *ptr, uns allow_dynamic);
145
146 static char *
147 interpret_section(struct cf_section *sec, int number, char **pars, int *processed, void *ptr, uns allow_dynamic)
148 {
149   cf_add_dirty(sec, ptr);
150   *processed = 0;
151   for (struct cf_item *ci=sec->cfg; ci->cls; ci++)
152   {
153     int taken;
154     char *msg = interpret_set_item(ci, number, pars, &taken, ptr + (uintptr_t) ci->ptr, allow_dynamic && !ci[1].cls);
155     if (msg)
156       return cf_printf("Item %s: %s", ci->name, msg);
157     *processed += taken;
158     number -= taken;
159     pars += taken;
160     if (!number)                // stop parsing, because many parsers would otherwise complain that number==0
161       break;
162   }
163   return NULL;
164 }
165
166 static void
167 add_to_list(cnode *where, cnode *new_node, enum cf_operation op)
168 {
169   switch (op)
170   {
171     case OP_EDIT:               // editation has been done in-place
172       break;
173     case OP_REMOVE:
174       CF_JOURNAL_VAR(where->prev->next);
175       CF_JOURNAL_VAR(where->next->prev);
176       clist_remove(where);
177       break;
178     case OP_AFTER:              // implementation dependent (prepend_head = after(list)), and where==list, see clists.h:74
179     case OP_PREPEND:
180     case OP_COPY:
181       CF_JOURNAL_VAR(where->next->prev);
182       CF_JOURNAL_VAR(where->next);
183       clist_insert_after(new_node, where);
184       break;
185     case OP_BEFORE:             // implementation dependent (append_tail = before(list))
186     case OP_APPEND:
187     case OP_SET:
188       CF_JOURNAL_VAR(where->prev->next);
189       CF_JOURNAL_VAR(where->prev);
190       clist_insert_before(new_node, where);
191       break;
192     default:
193       ASSERT(0);
194   }
195 }
196
197 static char *
198 interpret_add_list(struct cf_item *item, int number, char **pars, int *processed, void *ptr, enum cf_operation op)
199 {
200   if (op >= OP_REMOVE)
201     return cf_printf("You have to open a block for operation %s", cf_op_names[op]);
202   if (!number)
203     return "Nothing to add to the list";
204   struct cf_section *sec = item->u.sec;
205   *processed = 0;
206   uns index = 0;
207   while (number > 0)
208   {
209     void *node = cf_malloc(sec->size);
210     cf_init_section(item->name, sec, node, 1);
211     add_to_list(ptr, node, op);
212     int taken;
213     /* If the node contains any dynamic attribute at the end, we suppress
214      * auto-repetition here and pass the flag inside instead.  */
215     index++;
216     char *msg = interpret_section(sec, number, pars, &taken, node, sec->flags & SEC_FLAG_DYNAMIC);
217     if (msg)
218       return sec->flags & SEC_FLAG_DYNAMIC ? msg : cf_printf("Node %d of list %s: %s", index, item->name, msg);
219     *processed += taken;
220     number -= taken;
221     pars += taken;
222     if (sec->flags & SEC_FLAG_DYNAMIC)
223       break;
224   }
225   return NULL;
226 }
227
228 static char *
229 interpret_add_bitmap(struct cf_item *item, int number, char **pars, int *processed, u32 *ptr, enum cf_operation op)
230 {
231   if (op == OP_PREPEND || op == OP_APPEND)
232     op = OP_SET;
233   if (op != OP_SET && op != OP_REMOVE)
234     return cf_printf("Cannot apply operation %s on a bitmap", cf_op_names[op]);
235   else if (item->type != CT_INT && item->type != CT_LOOKUP)
236     return cf_printf("Type %s cannot be used with bitmaps", cf_type_names[item->type]);
237   cf_journal_block(ptr, sizeof(u32));
238   for (int i=0; i<number; i++) {
239     uns idx;
240     if (item->type == CT_INT)
241       TRY( cf_parse_int(pars[i], &idx) );
242     else
243       TRY( cf_parse_lookup(pars[i], &idx, item->u.lookup) );
244     if (idx >= 32)
245       return "Bitmaps only have 32 bits";
246     if (op == OP_SET)
247       *ptr |= 1<<idx;
248     else
249       *ptr &= ~(1<<idx);
250   }
251   *processed = number;
252   return NULL;
253 }
254
255 static char *
256 interpret_set_item(struct cf_item *item, int number, char **pars, int *processed, void *ptr, uns allow_dynamic)
257 {
258   int taken;
259   switch (item->cls)
260   {
261     case CC_STATIC:
262       if (!number)
263         return "Missing value";
264       taken = MIN(number, item->number);
265       *processed = taken;
266       uns size = cf_type_size(item->type, item->u.utype);
267       cf_journal_block(ptr, taken * size);
268       return cf_parse_ary(taken, pars, ptr, item->type, &item->u);
269     case CC_DYNAMIC:
270       if (!allow_dynamic)
271         return "Dynamic array cannot be used here";
272       taken = MIN(number, ABS(item->number));
273       *processed = taken;
274       return interpret_set_dynamic(item, taken, pars, ptr);
275     case CC_PARSER:
276       if (item->number < 0 && !allow_dynamic)
277         return "Parsers with variable number of parameters cannot be used here";
278       if (item->number > 0 && number < item->number)
279         return "Not enough parameters available for the parser";
280       taken = MIN(number, ABS(item->number));
281       *processed = taken;
282       for (int i=0; i<taken; i++)
283         pars[i] = cf_strdup(pars[i]);
284       return item->u.par(taken, pars, ptr);
285     case CC_SECTION:
286       return interpret_section(item->u.sec, number, pars, processed, ptr, allow_dynamic);
287     case CC_LIST:
288       if (!allow_dynamic)
289         return "Lists cannot be used here";
290       return interpret_add_list(item, number, pars, processed, ptr, OP_SET);
291     case CC_BITMAP:
292       if (!allow_dynamic)
293         return "Bitmaps cannot be used here";
294       return interpret_add_bitmap(item, number, pars, processed, ptr, OP_SET);
295     default:
296       ASSERT(0);
297   }
298 }
299
300 static char *
301 interpret_set_all(struct cf_item *item, void *ptr, enum cf_operation op)
302 {
303   if (item->cls == CC_BITMAP) {
304     cf_journal_block(ptr, sizeof(u32));
305     if (op == OP_CLEAR)
306       * (u32*) ptr = 0;
307     else
308       if (item->type == CT_INT)
309         * (u32*) ptr = ~0u;
310       else {
311         uns nr = -1;
312         while (item->u.lookup[++nr]);
313         * (u32*) ptr = ~0u >> (32-nr);
314       }
315     return NULL;
316   } else if (op != OP_CLEAR)
317     return "The item is not a bitmap";
318
319   if (item->cls == CC_LIST) {
320     cf_journal_block(ptr, sizeof(clist));
321     clist_init(ptr);
322   } else if (item->cls == CC_DYNAMIC) {
323     cf_journal_block(ptr, sizeof(void *));
324     static uns zero = 0;
325     * (void**) ptr = (&zero) + 1;
326   } else if (item->cls == CC_STATIC && item->type == CT_STRING) {
327     cf_journal_block(ptr, item->number * sizeof(char*));
328     bzero(ptr, item->number * sizeof(char*));
329   } else
330     return "The item is not a list, dynamic array, bitmap, or string";
331   return NULL;
332 }
333
334 static int
335 cmp_items(void *i1, void *i2, struct cf_item *item)
336 {
337   ASSERT(item->cls == CC_STATIC);
338   i1 += (uintptr_t) item->ptr;
339   i2 += (uintptr_t) item->ptr;
340   if (item->type == CT_STRING)
341     return strcmp(* (char**) i1, * (char**) i2);
342   else                          // all numeric types
343     return memcmp(i1, i2, cf_type_size(item->type, item->u.utype));
344 }
345
346 static void *
347 find_list_node(clist *list, void *query, struct cf_section *sec, u32 mask)
348 {
349   CLIST_FOR_EACH(cnode *, n, *list)
350   {
351     uns found = 1;
352     for (uns i=0; i<32; i++)
353       if (mask & (1<<i))
354         if (cmp_items(n, query, sec->cfg+i))
355         {
356           found = 0;
357           break;
358         }
359     if (found)
360       return n;
361   }
362   return NULL;
363 }
364
365 static char *
366 record_selector(struct cf_item *item, struct cf_section *sec, u32 *mask)
367 {
368   uns nr = sec->flags & SEC_FLAG_NUMBER;
369   if (item >= sec->cfg && item < sec->cfg + nr) // setting an attribute relative to this section
370   {
371     uns i = item - sec->cfg;
372     if (i >= 32)
373       return "Cannot select list nodes by this attribute";
374     if (sec->cfg[i].cls != CC_STATIC)
375       return "Selection can only be done based on basic attributes";
376     *mask |= 1 << i;
377   }
378   return NULL;
379 }
380
381 #define MAX_STACK_SIZE  10
382 static struct item_stack {
383   struct cf_section *sec;       // nested section
384   void *base_ptr;               // because original pointers are often relative
385   enum cf_operation op;         // it is performed when a closing brace is encountered
386   void *list;                   // list the operations should be done on
387   u32 mask;                     // bit array of selectors searching in a list
388   struct cf_item *item;         // cf_item of the list
389 } stack[MAX_STACK_SIZE];
390 static uns level;
391
392 static char *
393 opening_brace(struct cf_item *item, void *ptr, enum cf_operation op)
394 {
395   if (level >= MAX_STACK_SIZE-1)
396     return "Too many nested sections";
397   enum cf_operation pure_op = op & OP_MASK;
398   stack[++level] = (struct item_stack) {
399     .sec = NULL,
400     .base_ptr = NULL,
401     .op = pure_op,
402     .list = NULL,
403     .mask = 0,
404     .item = NULL,
405   };
406   if (!item)                    // unknown is ignored; we just need to trace recursion
407     return NULL;
408   stack[level].sec = item->u.sec;
409   if (item->cls == CC_SECTION)
410   {
411     if (pure_op != OP_SET)
412       return "Only SET operation can be used with a section";
413     stack[level].base_ptr = ptr;
414     stack[level].op = OP_EDIT | OP_2ND; // this list operation does nothing
415   }
416   else if (item->cls == CC_LIST)
417   {
418     stack[level].base_ptr = cf_malloc(item->u.sec->size);
419     cf_init_section(item->name, item->u.sec, stack[level].base_ptr, 1);
420     stack[level].list = ptr;
421     stack[level].item = item;
422     if (pure_op == OP_ALL)
423       return "Operation ALL cannot be applied on lists";
424     else if (pure_op < OP_REMOVE) {
425       add_to_list(ptr, stack[level].base_ptr, pure_op);
426       stack[level].op |= OP_2ND;
427     } else
428       stack[level].op |= OP_1ST;
429   }
430   else
431     return "Opening brace can only be used on sections and lists";
432   return NULL;
433 }
434
435 static char *
436 closing_brace(struct item_stack *st, enum cf_operation op, int number, char **pars)
437 {
438   if (st->op == OP_CLOSE)       // top-level
439     return "Unmatched } parenthesis";
440   if (!st->sec) {               // dummy run on unknown section
441     if (!(op & OP_OPEN))
442       level--;
443     return NULL;
444   }
445   enum cf_operation pure_op = st->op & OP_MASK;
446   if (st->op & OP_1ST)
447   {
448     st->list = find_list_node(st->list, st->base_ptr, st->sec, st->mask);
449     if (!st->list)
450       return "Cannot find a node matching the query";
451     if (pure_op != OP_REMOVE)
452     {
453       if (pure_op == OP_EDIT)
454         st->base_ptr = st->list;
455       else if (pure_op == OP_AFTER || pure_op == OP_BEFORE)
456         cf_init_section(st->item->name, st->sec, st->base_ptr, 1);
457       else if (pure_op == OP_COPY) {
458         if (st->sec->flags & SEC_FLAG_CANT_COPY)
459           return cf_printf("Item %s cannot be copied", st->item->name);
460         memcpy(st->base_ptr, st->list, st->sec->size);  // strings and dynamic arrays are shared
461         if (st->sec->copy)
462           TRY( st->sec->copy(st->base_ptr, st->list) );
463       } else
464         ASSERT(0);
465       if (op & OP_OPEN) {       // stay at the same recursion level
466         st->op = (st->op | OP_2ND) & ~OP_1ST;
467         add_to_list(st->list, st->base_ptr, pure_op);
468         return NULL;
469       }
470       int taken;                // parse parameters on 1 line immediately
471       TRY( interpret_section(st->sec, number, pars, &taken, st->base_ptr, 1) );
472       number -= taken;
473       pars += taken;
474       // and fall-thru to the 2nd phase
475     }
476     add_to_list(st->list, st->base_ptr, pure_op);
477   }
478   level--;
479   if (number)
480     return "No parameters expected after the }";
481   else if (op & OP_OPEN)
482     return "No { is expected";
483   else
484     return NULL;
485 }
486
487 static struct cf_item *
488 find_item(struct cf_section *curr_sec, const char *name, char **msg, void **ptr)
489 {
490   *msg = NULL;
491   if (name[0] == '^')                           // absolute name instead of relative
492     name++, curr_sec = &cf_sections, *ptr = NULL;
493   if (!curr_sec)                                // don't even search in an unknown section
494     return NULL;
495   while (1)
496   {
497     if (curr_sec != &cf_sections)
498       cf_add_dirty(curr_sec, *ptr);
499     char *c = strchr(name, '.');
500     if (c)
501       *c++ = 0;
502     struct cf_item *ci = cf_find_subitem(curr_sec, name);
503     if (!ci->cls)
504     {
505       if (!(curr_sec->flags & SEC_FLAG_UNKNOWN))        // ignore silently unknown top-level sections and unknown attributes in flagged sections
506         *msg = cf_printf("Unknown item %s", name);
507       return NULL;
508     }
509     *ptr += (uintptr_t) ci->ptr;
510     if (!c)
511       return ci;
512     if (ci->cls != CC_SECTION)
513     {
514       *msg = cf_printf("Item %s is not a section", name);
515       return NULL;
516     }
517     curr_sec = ci->u.sec;
518     name = c;
519   }
520 }
521
522 static char *
523 interpret_add(char *name, struct cf_item *item, int number, char **pars, int *takenp, void *ptr, enum cf_operation op)
524 {
525   switch (item->cls) {
526     case CC_DYNAMIC:
527       return interpret_add_dynamic(item, number, pars, takenp, ptr, op);
528     case CC_LIST:
529       return interpret_add_list(item, number, pars, takenp, ptr, op);
530     case CC_BITMAP:
531       return interpret_add_bitmap(item, number, pars, takenp, ptr, op);
532     default:
533       return cf_printf("Operation %s not supported on attribute %s", cf_op_names[op], name);
534   }
535 }
536
537 char *
538 cf_interpret_line(char *name, enum cf_operation op, int number, char **pars)
539 {
540   char *msg;
541   if ((op & OP_MASK) == OP_CLOSE)
542     return closing_brace(stack+level, op, number, pars);
543   void *ptr = stack[level].base_ptr;
544   struct cf_item *item = find_item(stack[level].sec, name, &msg, &ptr);
545   if (msg)
546     return msg;
547   if (stack[level].op & OP_1ST)
548     TRY( record_selector(item, stack[level].sec, &stack[level].mask) );
549   if (op & OP_OPEN) {           // the operation will be performed after the closing brace
550     if (number)
551       return "Cannot open a block after a parameter has been passed on a line";
552     return opening_brace(item, ptr, op);
553   }
554   if (!item)                    // ignored item in an unknown section
555     return NULL;
556   op &= OP_MASK;
557
558   int taken = 0;                // process as many parameters as possible
559   switch (op) {
560     case OP_CLEAR:
561     case OP_ALL:
562       msg = interpret_set_all(item, ptr, op);
563       break;
564     case OP_SET:
565       msg = interpret_set_item(item, number, pars, &taken, ptr, 1);
566       break;
567     case OP_RESET:
568       msg = interpret_set_all(item, ptr, OP_CLEAR);
569       if (!msg)
570         msg = interpret_add(name, item, number, pars, &taken, ptr, OP_APPEND);
571       break;
572     default:
573       msg = interpret_add(name, item, number, pars, &taken, ptr, op);
574   }
575   if (msg)
576     return msg;
577   if (taken < number)
578     return cf_printf("Too many parameters: %d>%d", number, taken);
579
580   return NULL;
581 }
582
583 char *
584 cf_find_item(const char *name, struct cf_item *item)
585 {
586   char *msg;
587   void *ptr = NULL;
588   struct cf_item *ci = find_item(&cf_sections, name, &msg, &ptr);
589   if (msg)
590     return msg;
591   if (ci) {
592     *item = *ci;
593     item->ptr = ptr;
594   } else
595     bzero(item, sizeof(struct cf_item));
596   return NULL;
597 }
598
599 char *
600 cf_modify_item(struct cf_item *item, enum cf_operation op, int number, char **pars)
601 {
602   char *msg;
603   int taken = 0;
604   switch (op) {
605     case OP_SET:
606       msg = interpret_set_item(item, number, pars, &taken, item->ptr, 1);
607       break;
608     case OP_CLEAR:
609     case OP_ALL:
610       msg = interpret_set_all(item, item->ptr, op);
611       break;
612     case OP_APPEND:
613     case OP_PREPEND:
614       switch (item->cls) {
615         case CC_DYNAMIC:
616           msg = interpret_add_dynamic(item, number, pars, &taken, item->ptr, op);
617           break;
618         case CC_LIST:
619           msg = interpret_add_list(item, number, pars, &taken, item->ptr, op);
620           break;
621         case CC_BITMAP:
622           msg = interpret_add_bitmap(item, number, pars, &taken, item->ptr, op);
623           break;
624         default:
625           return "The attribute does not support append/prepend";
626       }
627       break;
628     case OP_REMOVE:
629       if (item->cls == CC_BITMAP)
630         msg = interpret_add_bitmap(item, number, pars, &taken, item->ptr, op);
631       else
632         return "Only applicable on bitmaps";
633       break;
634     default:
635       return "Unsupported operation";
636   }
637   if (msg)
638     return msg;
639   if (taken < number)
640     return "Too many parameters";
641   return NULL;
642 }
643
644 void
645 cf_init_stack(void)
646 {
647   static uns initialized = 0;
648   if (!initialized++) {
649     cf_sections.flags |= SEC_FLAG_UNKNOWN;
650     cf_sections.size = 0;                       // size of allocated array used to be stored here
651     cf_init_section(NULL, &cf_sections, NULL, 0);
652   }
653   level = 0;
654   stack[0] = (struct item_stack) {
655     .sec = &cf_sections,
656     .base_ptr = NULL,
657     .op = OP_CLOSE,
658     .list = NULL,
659     .mask = 0,
660     .item = NULL
661   };
662 }
663
664 int
665 cf_check_stack(void)
666 {
667   if (level > 0) {
668     msg(L_ERROR, "Unterminated block");
669     return 1;
670   }
671   return 0;
672 }
673