4600b1b5f37079d69373af12ee2ebb854fcf9682
[oonf.git] / src / libconfig / cfg_cmd.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
4  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 /**
43  * @file
44  */
45
46 #include <regex.h>
47 #include <stdlib.h>
48 #include <strings.h>
49
50 #include <oonf/libcommon/autobuf.h>
51 #include <oonf/oonf.h>
52 #include <oonf/libconfig/cfg_cmd.h>
53 #include <oonf/libconfig/cfg_help.h>
54 #include <oonf/libconfig/cfg_io.h>
55
56 /**
57  *  contains data parsing logic */
58 struct _parsed_argument {
59   /*! pointer to null terminated section type, NULL if no section type */
60   char *section_type;
61
62   /*! pointer to null terminated section name, NULL if no section name */
63   char *section_name;
64
65   /*! pointer to null terminated key, NULL if no key */
66   char *entry_key;
67
68   /*! pointer to null terminated value, NULL if no value */
69   char *entry_value;
70 };
71
72 static int _print_schema_section(struct autobuf *log, struct cfg_db *db, const char *section);
73 static int _print_schema_entry(struct autobuf *log, struct cfg_db *db, const char *section, const char *entry);
74 static int _do_parse_arg(char *arg, struct _parsed_argument *pa, struct autobuf *log);
75
76 /**
77  * Implements the 'set' command for the command line
78  * @param instance pointer to cfg_instance
79  * @param db pointer to cfg_db to be modified
80  * @param arg argument of command
81  * @param log pointer for logging
82  * @return 0 if succeeded, -1 otherwise
83  */
84 int
85 cfg_cmd_handle_set(
86   struct cfg_instance *instance __attribute__((unused)), struct cfg_db *db, const char *arg, struct autobuf *log) {
87   struct _parsed_argument pa;
88   char *ptr;
89   bool dummy;
90   int result;
91
92   /* get temporary copy of argument string */
93   ptr = strdup(arg);
94   if (!ptr)
95     return -1;
96
97   /* prepare for cleanup */
98   result = -1;
99
100   if (_do_parse_arg(ptr, &pa, log)) {
101     goto handle_set_cleanup;
102   }
103
104   if (pa.entry_value != NULL) {
105     if (cfg_db_set_entry(db, pa.section_type, pa.section_name, pa.entry_key, pa.entry_value, true)) {
106       result = 0;
107     }
108     else {
109       cfg_append_printable_line(log, "Cannot create entry: '%s'\n", arg);
110     }
111     result = 0;
112     goto handle_set_cleanup;
113   }
114
115   if (pa.entry_key != NULL) {
116     cfg_append_printable_line(log, "Key without value is not allowed for set command: %s", arg);
117     goto handle_set_cleanup;
118   }
119
120   /* set section */
121   if (NULL == _cfg_db_add_section(db, pa.section_type, pa.section_name, &dummy)) {
122     cfg_append_printable_line(log, "Cannot create section: '%s'\n", arg);
123     goto handle_set_cleanup;
124   }
125   result = 0;
126
127 handle_set_cleanup:
128   free(ptr);
129   return result;
130 }
131
132 /**
133  * Implements the 'remove' command for the command line
134  * @param instance pointer to cfg_instance
135  * @param db pointer to cfg_db to be modified
136  * @param arg argument of command
137  * @param log pointer for logging
138  * @return 0 if succeeded, -1 otherwise
139  */
140 int
141 cfg_cmd_handle_remove(
142   struct cfg_instance *instance __attribute__((unused)), struct cfg_db *db, const char *arg, struct autobuf *log) {
143   struct _parsed_argument pa;
144   char *ptr;
145   int result;
146
147   /* get temporary copy of argument string */
148   ptr = strdup(arg);
149   if (!ptr)
150     return -1;
151
152   /* prepare for cleanup */
153   result = -1;
154
155   if (_do_parse_arg(ptr, &pa, log)) {
156     goto handle_remove_cleanup;
157   }
158
159   if (pa.entry_value != NULL) {
160     cfg_append_printable_line(log, "Value is not allowed for remove command: %s", arg);
161     goto handle_remove_cleanup;
162   }
163
164   if (pa.entry_key != NULL) {
165     if (!cfg_db_remove_entry(db, pa.section_type, pa.section_name, pa.entry_key)) {
166       result = 0;
167     }
168     else {
169       cfg_append_printable_line(log, "Cannot remove entry: '%s'\n", arg);
170     }
171     goto handle_remove_cleanup;
172   }
173
174   if (pa.section_name) {
175     if (cfg_db_remove_namedsection(db, pa.section_type, pa.section_name)) {
176       cfg_append_printable_line(log, "Cannot remove section: '%s'\n", arg);
177       goto handle_remove_cleanup;
178     }
179   }
180
181   if (pa.section_type) {
182     if (cfg_db_remove_sectiontype(db, pa.section_type)) {
183       cfg_append_printable_line(log, "Cannot remove section: '%s'\n", arg);
184       goto handle_remove_cleanup;
185     }
186   }
187   result = 0;
188
189 handle_remove_cleanup:
190   free(ptr);
191   return result;
192 }
193
194 /**
195  * Implements the 'get' command for the command line
196  * @param instance pointer to cfg_instance
197  * @param db pointer to cfg_db to be modified
198  * @param arg argument of command
199  * @param log pointer for logging
200  * @return 0 if succeeded, -1 otherwise
201  */
202 int
203 cfg_cmd_handle_get(
204   struct cfg_instance *instance __attribute__((unused)), struct cfg_db *db, const char *arg, struct autobuf *log) {
205   struct cfg_section_type *type, *type_it;
206   struct cfg_named_section *named, *named_it;
207   struct cfg_entry *entry, *entry_it;
208   struct _parsed_argument pa;
209   char *arg_copy, *tmp;
210   int result;
211
212   if (arg == NULL || *arg == 0) {
213     cfg_append_printable_line(log, "Section types in database:");
214
215     CFG_FOR_ALL_SECTION_TYPES(db, type, type_it) {
216       cfg_append_printable_line(log, "%s", type->type);
217     }
218     return 0;
219   }
220
221   arg_copy = strdup(arg);
222   if (!arg_copy) {
223     return -1;
224   }
225
226   /* prepare for cleanup */
227   result = -1;
228
229   if (_do_parse_arg(arg_copy, &pa, log)) {
230     goto handle_get_cleanup;
231   }
232
233   if (pa.entry_value != NULL) {
234     cfg_append_printable_line(log, "Value is not allowed for view command: %s", arg);
235     goto handle_get_cleanup;
236   }
237
238   if (pa.entry_key != NULL) {
239     if (NULL == (entry = cfg_db_find_entry(db, pa.section_type, pa.section_name, pa.entry_key))) {
240       cfg_append_printable_line(log, "Cannot find data for entry: '%s'\n", arg);
241       goto handle_get_cleanup;
242     }
243
244     cfg_append_printable_line(log, "Key '%s' has value:", arg);
245     strarray_for_each_element(&entry->val, tmp) {
246       cfg_append_printable_line(log, "%s", tmp);
247     }
248     result = 0;
249     goto handle_get_cleanup;
250   }
251
252   if (pa.section_name == NULL) {
253     type = cfg_db_find_sectiontype(db, pa.section_type);
254     if (type == NULL || type->names.count == 0) {
255       cfg_append_printable_line(log, "Cannot find data for section type: %s", arg);
256       goto handle_get_cleanup;
257     }
258
259     named = avl_first_element(&type->names, named, node);
260     if (cfg_db_is_named_section(named)) {
261       cfg_append_printable_line(log, "Named sections in section type: %s", pa.section_type);
262       CFG_FOR_ALL_SECTION_NAMES(type, named, named_it) {
263         cfg_append_printable_line(log, "%s", named->name);
264       }
265       result = 0;
266       goto handle_get_cleanup;
267     }
268   }
269
270   named = cfg_db_find_namedsection(db, pa.section_type, pa.section_name);
271   if (named == NULL) {
272     cfg_append_printable_line(log, "Cannot find data for section: %s", arg);
273     goto handle_get_cleanup;
274   }
275
276   cfg_append_printable_line(log, "Entry keys for section '%s':", arg);
277   CFG_FOR_ALL_ENTRIES(named, entry, entry_it) {
278     cfg_append_printable_line(log, "%s", entry->name);
279   }
280   result = 0;
281
282 handle_get_cleanup:
283   free(arg_copy);
284   return result;
285 }
286
287 /**
288  * Implements the 'load' command for the command line
289  * @param instance pointer to cfg_instance
290  * @param db pointer to cfg_db to be modified
291  * @param arg argument of command
292  * @param log pointer for logging
293  * @return 0 if succeeded, -1 otherwise
294  */
295 int
296 cfg_cmd_handle_load(struct cfg_instance *instance, struct cfg_db *db, const char *arg, struct autobuf *log) {
297   struct cfg_db *temp_db;
298
299   temp_db = cfg_io_load(instance, arg, log);
300   if (temp_db != NULL) {
301     cfg_db_copy(db, temp_db);
302     cfg_db_remove(temp_db);
303   }
304   return temp_db != NULL ? 0 : -1;
305 }
306
307 /**
308  * Implements the 'save' command for the command line
309  * @param instance pointer to cfg_instance
310  * @param db pointer to cfg_db to be modified
311  * @param arg argument of command
312  * @param log pointer for logging
313  * @return 0 if succeeded, -1 otherwise
314  */
315 int
316 cfg_cmd_handle_save(struct cfg_instance *instance, struct cfg_db *db, const char *arg, struct autobuf *log) {
317   return cfg_io_save(instance, arg, db, log);
318 }
319
320 /**
321  * Implements the 'schema' command for the configuration system
322  * @param db pointer to cfg_db to be modified
323  * @param arg argument of command
324  * @param log pointer for logging
325  * @return 0 if succeeded, -1 otherwise
326  */
327 int
328 cfg_cmd_handle_schema(struct cfg_db *db, const char *arg, struct autobuf *log) {
329   struct cfg_schema_section *s_section;
330   char *copy, *ptr;
331   int result;
332
333   if (db->schema == NULL) {
334     abuf_puts(log, "Internal error, database not connected to schema\n");
335     return -1;
336   }
337
338   if (arg == NULL || *arg == 0) {
339     abuf_puts(log, "List of section types:\n"
340                    "(use this command with the types as parameter for more information)\n");
341     avl_for_each_element(&db->schema->sections, s_section, _section_node) {
342       if (!s_section->_section_node.follower) {
343         cfg_append_printable_line(log, CFG_HELP_INDENT_PREFIX "%s (%s)%s%s", s_section->type,
344           CFG_SCHEMA_SECTIONMODE[s_section->mode], s_section->help ? ": " : "", s_section->help ? s_section->help : "");
345       }
346       else if (s_section->help) {
347         cfg_append_printable_line(log, CFG_HELP_INDENT_PREFIX CFG_HELP_INDENT_PREFIX "%s", s_section->help);
348       }
349     }
350     return 0;
351   }
352
353   if (strcmp(arg, "all") == 0) {
354     const char *last_type = NULL;
355
356     avl_for_each_element(&db->schema->sections, s_section, _section_node) {
357       if (last_type == NULL || strcasecmp(s_section->type, last_type) != 0) {
358         if (last_type != NULL) {
359           abuf_puts(log, "\n");
360         }
361         _print_schema_section(log, db, s_section->type);
362         last_type = s_section->type;
363       }
364     }
365     return 0;
366   }
367
368   /* copy string into stack*/
369   copy = strdup(arg);
370
371   /* prepare for cleanup */
372   result = -1;
373
374   ptr = strchr(copy, '.');
375   if (ptr) {
376     *ptr++ = 0;
377   }
378
379   if (ptr == NULL) {
380     result = _print_schema_section(log, db, copy);
381   }
382   else {
383     result = _print_schema_entry(log, db, copy, ptr);
384   }
385
386   free(copy);
387   return result;
388 }
389
390 static int
391 _print_schema_section(struct autobuf *log, struct cfg_db *db, const char *section) {
392   struct cfg_schema_entry *s_entry, *s_entry_it;
393   struct cfg_schema_entry_key key;
394
395   /* show all schema entries for a section */
396   key.type = section;
397   key.entry = NULL;
398
399   s_entry = avl_find_ge_element(&db->schema->entries, &key, s_entry, _node);
400   if (s_entry == NULL || cfg_cmp_keys(s_entry->key.type, section) != 0) {
401     cfg_append_printable_line(log, "Unknown section type '%s'", section);
402     return -1;
403   }
404
405   if (s_entry->_parent->mode == CFG_SSMODE_NAMED_WITH_DEFAULT) {
406     cfg_append_printable_line(
407       log, "Section '%s' has default name '%s'", s_entry->_parent->type, s_entry->_parent->def_name);
408   }
409
410   if (s_entry->_parent->help) {
411     cfg_append_printable_line(log, "%s", s_entry->_parent->help);
412   }
413
414   cfg_append_printable_line(log, "List of entries in section type '%s':", section);
415   abuf_puts(log, "(use this command with 'type.name' as parameter for more information)\n");
416
417   s_entry_it = s_entry;
418   avl_for_element_to_last(&db->schema->entries, s_entry, s_entry_it, _node) {
419     if (cfg_cmp_keys(s_entry_it->key.type, section) != 0) {
420       break;
421     }
422
423     if (!s_entry_it->_node.follower) {
424       cfg_append_printable_line(log, CFG_HELP_INDENT_PREFIX "%s%s%s", s_entry_it->key.entry,
425         strarray_is_empty_c(&s_entry_it->def) ? " (mandatory)" : "", s_entry_it->list ? " (list)" : "");
426     }
427 #if !defined(REMOVE_HELPTEXT)
428     if (s_entry_it->help) {
429       cfg_append_printable_line(log, CFG_HELP_INDENT_PREFIX CFG_HELP_INDENT_PREFIX "%s", s_entry_it->help);
430     }
431 #endif
432   }
433   return 0;
434 }
435
436 static int
437 _print_schema_entry(struct autobuf *log, struct cfg_db *db, const char *section, const char *entry) {
438   struct cfg_schema_entry *s_entry_it, *s_entry_first, *s_entry_last;
439   struct cfg_schema_entry_key key;
440   const char *c_ptr;
441
442   /* show all schema entries of a type/entry pair */
443   key.type = section;
444   key.entry = entry;
445
446   s_entry_last = NULL;
447
448   avl_for_each_elements_with_key(&db->schema->entries, s_entry_it, _node, s_entry_first, &key) {
449     if (s_entry_it == s_entry_first) {
450       /* print type/parameter */
451       cfg_append_printable_line(log, CFG_HELP_INDENT_PREFIX "%s%s%s", s_entry_it->key.entry,
452         strarray_is_empty_c(&s_entry_it->def) ? " (mandatory)" : "", s_entry_it->list ? " (list)" : "");
453
454       /* print defaults */
455       if (!strarray_is_empty_c(&s_entry_it->def)) {
456         cfg_append_printable_line(log, CFG_HELP_INDENT_PREFIX "Default value:");
457         strarray_for_each_element(&s_entry_it->def, c_ptr) {
458           cfg_append_printable_line(log, CFG_HELP_INDENT_PREFIX CFG_HELP_INDENT_PREFIX "'%s'", c_ptr);
459         }
460       }
461     }
462
463     if (s_entry_it->cb_valhelp) {
464       /* print validator help if different from last validator */
465       if (s_entry_last == NULL || s_entry_last->cb_valhelp != s_entry_it->cb_valhelp ||
466           memcmp(&s_entry_last->validate_param, &s_entry_it->validate_param, sizeof(s_entry_it->validate_param)) != 0) {
467         s_entry_it->cb_valhelp(s_entry_it, log);
468         s_entry_last = s_entry_it;
469       }
470     }
471   }
472
473 #if !defined(REMOVE_HELPTEXT)
474   avl_for_each_elements_with_key(&db->schema->entries, s_entry_it, _node, s_entry_first, &key) {
475     /* print help text */
476     if (s_entry_it->help) {
477       if (s_entry_it == s_entry_first) {
478         abuf_puts(log, CFG_HELP_INDENT_PREFIX "Description:\n");
479       }
480       cfg_append_printable_line(log, CFG_HELP_INDENT_PREFIX CFG_HELP_INDENT_PREFIX "%s", s_entry_it->help);
481     }
482   }
483 #endif
484   return 0;
485 }
486
487 /**
488  * Parse the parameter string for most commands
489  * @param arg argument of command
490  * @param pa pointer to parsed argument struct for more return data
491  * @param log pointer for logging
492  * @return 0 if succeeded, negative otherwise
493  */
494 static int
495 _do_parse_arg(char *arg, struct _parsed_argument *pa, struct autobuf *log) {
496   static const char *pattern = "^(([a-zA-Z_][a-zA-Z_0-9]*)(\\[([^]]*)\\])?\\.)?([a-zA-Z_][a-zA-Z_0-9]*)?(=(.*))?$";
497   regex_t regexp;
498   regmatch_t matchers[8];
499
500   if (regcomp(&regexp, pattern, REG_EXTENDED)) {
501     /* error in regexp implementation */
502     cfg_append_printable_line(log, "Error while formatting regular expression for parsing.");
503     return -2;
504   }
505
506   if (regexec(&regexp, arg, ARRAYSIZE(matchers), matchers, 0)) {
507     cfg_append_printable_line(log, "Illegal input for command: %s", arg);
508     regfree(&regexp);
509     return -1;
510   }
511
512   memset(pa, 0, sizeof(*pa));
513   if (matchers[2].rm_so != -1) {
514     pa->section_type = &arg[matchers[2].rm_so];
515     arg[matchers[2].rm_eo] = 0;
516   }
517   if (matchers[4].rm_so != -1) {
518     pa->section_name = &arg[matchers[4].rm_so];
519     arg[matchers[4].rm_eo] = 0;
520   }
521   if (matchers[5].rm_so != -1) {
522     pa->entry_key = &arg[matchers[5].rm_so];
523     arg[matchers[5].rm_eo] = 0;
524   }
525   if (matchers[7].rm_so != -1) {
526     pa->entry_value = &arg[matchers[7].rm_so];
527   }
528
529   regfree(&regexp);
530   return 0;
531 }