Add "config query" command to remotecontrol plugin to query a configuration value...
[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 'query' 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_query(
297   struct cfg_instance *instance __attribute__((unused)), struct cfg_db *db, const char *arg, struct autobuf *log) {
298   struct _parsed_argument pa;
299   const struct const_strarray *array;
300   const char *tmp;
301   char *arg_copy;
302   int result;
303
304   if (arg == NULL || *arg == 0) {
305     cfg_append_printable_line(log, "Query needs section and key, but no value: %s", arg);
306     return 0;
307   }
308
309   arg_copy = strdup(arg);
310   if (!arg_copy) {
311     return -1;
312   }
313
314   /* prepare for cleanup */
315   result = -1;
316
317   if (_do_parse_arg(arg_copy, &pa, log)) {
318     goto handle_get_cleanup;
319   }
320
321   if (pa.entry_key == NULL || pa.entry_value != NULL) {
322     cfg_append_printable_line(log, "Query needs section and key, but no value: %s", arg);
323     goto handle_get_cleanup;
324   }
325
326   array = cfg_db_get_entry_value(db, pa.section_type, pa.section_name, pa.entry_key);
327   if (array) {
328     cfg_append_printable_line(log, "Key '%s' has value:", arg);
329     strarray_for_each_element(array, tmp) {
330       cfg_append_printable_line(log, "%s", tmp);
331     }
332     result = 0;
333   }
334   else {
335     cfg_append_printable_line(log, "Key '%s' has no value:", arg);
336   }
337
338 handle_get_cleanup:
339   free(arg_copy);
340   return result;
341 }
342
343 /**
344  * Implements the 'load' command for the command line
345  * @param instance pointer to cfg_instance
346  * @param db pointer to cfg_db to be modified
347  * @param arg argument of command
348  * @param log pointer for logging
349  * @return 0 if succeeded, -1 otherwise
350  */
351 int
352 cfg_cmd_handle_load(struct cfg_instance *instance, struct cfg_db *db, const char *arg, struct autobuf *log) {
353   struct cfg_db *temp_db;
354
355   temp_db = cfg_io_load(instance, arg, log);
356   if (temp_db != NULL) {
357     cfg_db_copy(db, temp_db);
358     cfg_db_remove(temp_db);
359   }
360   return temp_db != NULL ? 0 : -1;
361 }
362
363 /**
364  * Implements the 'save' command for the command line
365  * @param instance pointer to cfg_instance
366  * @param db pointer to cfg_db to be modified
367  * @param arg argument of command
368  * @param log pointer for logging
369  * @return 0 if succeeded, -1 otherwise
370  */
371 int
372 cfg_cmd_handle_save(struct cfg_instance *instance, struct cfg_db *db, const char *arg, struct autobuf *log) {
373   return cfg_io_save(instance, arg, db, log);
374 }
375
376 /**
377  * Implements the 'schema' command for the configuration system
378  * @param db pointer to cfg_db to be modified
379  * @param arg argument of command
380  * @param log pointer for logging
381  * @return 0 if succeeded, -1 otherwise
382  */
383 int
384 cfg_cmd_handle_schema(struct cfg_db *db, const char *arg, struct autobuf *log) {
385   struct cfg_schema_section *s_section;
386   char *copy, *ptr;
387   int result;
388
389   if (db->schema == NULL) {
390     abuf_puts(log, "Internal error, database not connected to schema\n");
391     return -1;
392   }
393
394   if (arg == NULL || *arg == 0) {
395     abuf_puts(log, "List of section types:\n"
396                    "(use this command with the types as parameter for more information)\n");
397     avl_for_each_element(&db->schema->sections, s_section, _section_node) {
398       if (!s_section->_section_node.follower) {
399         cfg_append_printable_line(log, CFG_HELP_INDENT_PREFIX "%s (%s)%s%s", s_section->type,
400           CFG_SCHEMA_SECTIONMODE[s_section->mode], s_section->help ? ": " : "", s_section->help ? s_section->help : "");
401       }
402       else if (s_section->help) {
403         cfg_append_printable_line(log, CFG_HELP_INDENT_PREFIX CFG_HELP_INDENT_PREFIX "%s", s_section->help);
404       }
405     }
406     return 0;
407   }
408
409   if (strcmp(arg, "all") == 0) {
410     const char *last_type = NULL;
411
412     avl_for_each_element(&db->schema->sections, s_section, _section_node) {
413       if (last_type == NULL || strcasecmp(s_section->type, last_type) != 0) {
414         if (last_type != NULL) {
415           abuf_puts(log, "\n");
416         }
417         _print_schema_section(log, db, s_section->type);
418         last_type = s_section->type;
419       }
420     }
421     return 0;
422   }
423
424   /* copy string into stack*/
425   copy = strdup(arg);
426
427   /* prepare for cleanup */
428   result = -1;
429
430   ptr = strchr(copy, '.');
431   if (ptr) {
432     *ptr++ = 0;
433   }
434
435   if (ptr == NULL) {
436     result = _print_schema_section(log, db, copy);
437   }
438   else {
439     result = _print_schema_entry(log, db, copy, ptr);
440   }
441
442   free(copy);
443   return result;
444 }
445
446 static int
447 _print_schema_section(struct autobuf *log, struct cfg_db *db, const char *section) {
448   struct cfg_schema_entry *s_entry, *s_entry_it;
449   struct cfg_schema_entry_key key;
450
451   /* show all schema entries for a section */
452   key.type = section;
453   key.entry = NULL;
454
455   s_entry = avl_find_ge_element(&db->schema->entries, &key, s_entry, _node);
456   if (s_entry == NULL || cfg_cmp_keys(s_entry->key.type, section) != 0) {
457     cfg_append_printable_line(log, "Unknown section type '%s'", section);
458     return -1;
459   }
460
461   if (s_entry->_parent->mode == CFG_SSMODE_NAMED_WITH_DEFAULT) {
462     cfg_append_printable_line(
463       log, "Section '%s' has default name '%s'", s_entry->_parent->type, s_entry->_parent->def_name);
464   }
465
466   if (s_entry->_parent->help) {
467     cfg_append_printable_line(log, "%s", s_entry->_parent->help);
468   }
469
470   cfg_append_printable_line(log, "List of entries in section type '%s':", section);
471   abuf_puts(log, "(use this command with 'type.name' as parameter for more information)\n");
472
473   s_entry_it = s_entry;
474   avl_for_element_to_last(&db->schema->entries, s_entry, s_entry_it, _node) {
475     if (cfg_cmp_keys(s_entry_it->key.type, section) != 0) {
476       break;
477     }
478
479     if (!s_entry_it->_node.follower) {
480       cfg_append_printable_line(log, CFG_HELP_INDENT_PREFIX "%s%s%s", s_entry_it->key.entry,
481         strarray_is_empty_c(&s_entry_it->def) ? " (mandatory)" : "", s_entry_it->list ? " (list)" : "");
482     }
483 #if !defined(REMOVE_HELPTEXT)
484     if (s_entry_it->help) {
485       cfg_append_printable_line(log, CFG_HELP_INDENT_PREFIX CFG_HELP_INDENT_PREFIX "%s", s_entry_it->help);
486     }
487 #endif
488   }
489   return 0;
490 }
491
492 static int
493 _print_schema_entry(struct autobuf *log, struct cfg_db *db, const char *section, const char *entry) {
494   struct cfg_schema_entry *s_entry_it, *s_entry_first, *s_entry_last;
495   struct cfg_schema_entry_key key;
496   const char *c_ptr;
497
498   /* show all schema entries of a type/entry pair */
499   key.type = section;
500   key.entry = entry;
501
502   s_entry_last = NULL;
503
504   avl_for_each_elements_with_key(&db->schema->entries, s_entry_it, _node, s_entry_first, &key) {
505     if (s_entry_it == s_entry_first) {
506       /* print type/parameter */
507       cfg_append_printable_line(log, CFG_HELP_INDENT_PREFIX "%s%s%s", s_entry_it->key.entry,
508         strarray_is_empty_c(&s_entry_it->def) ? " (mandatory)" : "", s_entry_it->list ? " (list)" : "");
509
510       /* print defaults */
511       if (!strarray_is_empty_c(&s_entry_it->def)) {
512         cfg_append_printable_line(log, CFG_HELP_INDENT_PREFIX "Default value:");
513         strarray_for_each_element(&s_entry_it->def, c_ptr) {
514           cfg_append_printable_line(log, CFG_HELP_INDENT_PREFIX CFG_HELP_INDENT_PREFIX "'%s'", c_ptr);
515         }
516       }
517     }
518
519     if (s_entry_it->cb_valhelp) {
520       /* print validator help if different from last validator */
521       if (s_entry_last == NULL || s_entry_last->cb_valhelp != s_entry_it->cb_valhelp ||
522           memcmp(&s_entry_last->validate_param, &s_entry_it->validate_param, sizeof(s_entry_it->validate_param)) != 0) {
523         s_entry_it->cb_valhelp(s_entry_it, log);
524         s_entry_last = s_entry_it;
525       }
526     }
527   }
528
529 #if !defined(REMOVE_HELPTEXT)
530   avl_for_each_elements_with_key(&db->schema->entries, s_entry_it, _node, s_entry_first, &key) {
531     /* print help text */
532     if (s_entry_it->help) {
533       if (s_entry_it == s_entry_first) {
534         abuf_puts(log, CFG_HELP_INDENT_PREFIX "Description:\n");
535       }
536       cfg_append_printable_line(log, CFG_HELP_INDENT_PREFIX CFG_HELP_INDENT_PREFIX "%s", s_entry_it->help);
537     }
538   }
539 #endif
540   return 0;
541 }
542
543 /**
544  * Parse the parameter string for most commands
545  * @param arg argument of command
546  * @param pa pointer to parsed argument struct for more return data
547  * @param log pointer for logging
548  * @return 0 if succeeded, negative otherwise
549  */
550 static int
551 _do_parse_arg(char *arg, struct _parsed_argument *pa, struct autobuf *log) {
552   static const char *pattern = "^(([a-zA-Z_][a-zA-Z_0-9]*)(\\[([^]]*)\\])?\\.)?([a-zA-Z_][a-zA-Z_0-9]*)?(=(.*))?$";
553   regex_t regexp;
554   regmatch_t matchers[8];
555
556   if (regcomp(&regexp, pattern, REG_EXTENDED)) {
557     /* error in regexp implementation */
558     cfg_append_printable_line(log, "Error while formatting regular expression for parsing.");
559     return -2;
560   }
561
562   if (regexec(&regexp, arg, ARRAYSIZE(matchers), matchers, 0)) {
563     cfg_append_printable_line(log, "Illegal input for command: %s", arg);
564     regfree(&regexp);
565     return -1;
566   }
567
568   memset(pa, 0, sizeof(*pa));
569   if (matchers[2].rm_so != -1) {
570     pa->section_type = &arg[matchers[2].rm_so];
571     arg[matchers[2].rm_eo] = 0;
572   }
573   if (matchers[4].rm_so != -1) {
574     pa->section_name = &arg[matchers[4].rm_so];
575     arg[matchers[4].rm_eo] = 0;
576   }
577   if (matchers[5].rm_so != -1) {
578     pa->entry_key = &arg[matchers[5].rm_so];
579     arg[matchers[5].rm_eo] = 0;
580   }
581   if (matchers[7].rm_so != -1) {
582     pa->entry_value = &arg[matchers[7].rm_so];
583   }
584
585   regfree(&regexp);
586   return 0;
587 }