Fawkes API Fawkes Development Version
main.cpp
1
2/***************************************************************************
3 * main.cpp - Fawkes config tool
4 *
5 * Created: Mon Jan 08 16:43:45 2007
6 * Copyright 2006-2007 Tim Niemueller [www.niemueller.de]
7 *
8 ****************************************************************************/
9
10/* This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
19 *
20 * Read the full text in the LICENSE.GPL file in the doc directory.
21 */
22
23#include <config/change_handler.h>
24#include <config/netconf.h>
25#include <config/sqlite.h>
26#include <config/yaml.h>
27#include <netcomm/fawkes/client.h>
28#include <utils/system/argparser.h>
29#include <utils/system/signal.h>
30
31#include <cstdio>
32#include <cstdlib>
33#include <cstring>
34#include <fnmatch.h>
35#include <iostream>
36#include <unistd.h>
37
38using namespace fawkes;
39
40/** Tool to watch and output config changes.
41 */
43{
44public:
45 /** Constructor.
46 * @param config Configuration to watch
47 * @param c network client, thread is cancelled on signal
48 */
51 {
52 this->c = c;
53 this->config = config;
54 quit = false;
55 config->add_change_handler(this);
56 }
57
58 virtual void
59 handle_signal(int signal)
60 {
61 config->rem_change_handler(this);
62 quit = true;
63 }
64
65 virtual void
66 config_tag_changed(const char *new_tag)
67 {
68 printf("--> New tag loaded: %s\n", new_tag);
69 }
70
71 virtual void
73 {
74 printf("%s %-55s| %-8s| %-14s\n",
75 v->is_default() ? "*" : " ",
76 v->path(),
77 v->type(),
78 v->get_as_string().c_str());
79 }
80
81 virtual void
83 {
84 printf("%s %s: %s\n", v->is_default() ? "C" : "c", v->path(), v->get_comment().c_str());
85 }
86
87 virtual void
88 config_value_erased(const char *path)
89 {
90 printf(" %-55s| %-8s| %-14s\n", path, "", "ERASED");
91 }
92
93 /** Run.
94 * This joins the network thread.
95 */
96 void
98 {
99 while (!quit) {
100 if (c) {
101 c->wait(FAWKES_CID_CONFIGMANAGER);
102 } else {
103 usleep(500000);
104 }
105 }
106 }
107
108private:
110 Configuration * config;
111 bool quit;
112};
113
114/** Print header. */
115void
116print_header()
117{
118 printf("D %-55s| %-8s| %-14s\n", "Path", "Type", "Value");
119 printf(
120 "--------------------------------------------------------------------------------------\n");
121}
122
123/** Print a single value.
124 * @param i config item to print.
125 */
126void
127print_line(Configuration::ValueIterator *i, bool show_comment = false)
128{
129 if (i->is_list()) {
130 printf("%s %-55s| %-8s| LIST (values below)\n",
131 (i->is_default() ? "*" : " "),
132 i->path(),
133 i->type());
134 if (i->is_uint()) {
135 std::vector<unsigned int> values = i->get_uints();
136 for (size_t j = 0; j < values.size(); ++j) {
137 printf(" %-67s%-14u\n", "", values[j]);
138 }
139 } else if (i->is_int()) {
140 std::vector<int> values = i->get_ints();
141 for (size_t j = 0; j < values.size(); ++j) {
142 printf(" %-67s%-14i\n", "", values[j]);
143 }
144 } else if (i->is_bool()) {
145 std::vector<bool> values = i->get_bools();
146 for (size_t j = 0; j < values.size(); ++j) {
147 printf(" %-67s%-14s\n", "", values[j] ? "true" : "false");
148 }
149 } else if (i->is_float()) {
150 std::vector<float> values = i->get_floats();
151 for (size_t j = 0; j < values.size(); ++j) {
152 printf(" %-67s%-14f\n", "", values[j]);
153 }
154 } else if (i->is_string()) {
155 std::vector<std::string> values = i->get_strings();
156 for (size_t j = 0; j < values.size(); ++j) {
157 printf(" %-67s%-14s\n", "", values[j].c_str());
158 }
159 } else {
160 printf("%s %-55s| UNKNOWN LIST TYPE\n", (i->is_default() ? "*" : " "), i->path());
161 }
162 } else {
163 if (i->is_uint()) {
164 printf(
165 "%s %-55s| %-8s| %-14u\n", (i->is_default() ? "*" : " "), i->path(), "uint", i->get_uint());
166 } else if (i->is_int()) {
167 printf("%s %-55s| %-8s| %-14i\n",
168 (i->is_default() ? "*" : " "),
169 i->path(),
170 i->type(),
171 i->get_int());
172 } else if (i->is_bool()) {
173 printf("%s %-55s| %-8s| %-14s\n",
174 (i->is_default() ? "*" : " "),
175 i->path(),
176 i->type(),
177 (i->get_bool() ? "true" : "false"));
178 } else if (i->is_float()) {
179 printf("%s %-55s| %-8s| %-14f\n",
180 (i->is_default() ? "*" : " "),
181 i->path(),
182 i->type(),
183 i->get_float());
184 } else if (i->is_string()) {
185 printf("%s %-55s| %-8s| %-14s\n",
186 (i->is_default() ? "*" : " "),
187 i->path(),
188 i->type(),
189 i->get_string().c_str());
190 } else {
191 printf("%s %-55s| UNKNOWN TYPE\n", (i->is_default() ? "*" : " "), i->path());
192 }
193
194 if (show_comment) {
195 try {
196 std::string comment = i->get_comment();
197 if (comment != "") {
198 printf("C %-55s: %s\n", i->path(), comment.c_str());
199 }
200 } catch (Exception &e) {
201 // maybe there is no comment, ignore it...
202 }
203 }
204 }
205}
206
207/** Print a line of output.
208 * @param i config item to print.
209 */
210void
211print_value(Configuration::ValueIterator *i, bool show_comment = false)
212{
213 if (i->is_list()) {
214 if (i->is_uint()) {
215 for (const auto &v : i->get_uints())
216 printf("%u\n", v);
217 } else if (i->is_int()) {
218 for (const auto &v : i->get_ints())
219 printf("%d\n", v);
220 } else if (i->is_bool()) {
221 for (const auto &v : i->get_bools())
222 printf("%s\n", v ? "true" : "false");
223 } else if (i->is_float()) {
224 for (const auto &v : i->get_floats())
225 printf("%f\n", v);
226 } else if (i->is_string()) {
227 for (const auto &v : i->get_strings())
228 printf("%s\n", v.c_str());
229 }
230 } else {
231 if (i->is_uint()) {
232 printf("%-14u\n", i->get_uint());
233 } else if (i->is_int()) {
234 printf("%-14i\n", i->get_int());
235 } else if (i->is_bool()) {
236 printf("%-14s\n", (i->get_bool() ? "true" : "false"));
237 } else if (i->is_float()) {
238 printf("%-14f\n", i->get_float());
239 } else if (i->is_string()) {
240 printf("%-14s\n", i->get_string().c_str());
241 }
242 }
243}
244
245void
246print_usage(const char *program_name)
247{
248 std::cout << "Usage: " << program_name << " [options] <cmd>" << std::endl
249 << "where cmd is one of the following:" << std::endl
250 << std::endl
251 << " list" << std::endl
252 << " List all configuration items" << std::endl
253 << std::endl
254 << " watch" << std::endl
255 << " Watch configuration changes" << std::endl
256 << std::endl
257 << " get <path>" << std::endl
258 << " Get value for the given path" << std::endl
259 << std::endl
260 << " set <path> <value> [type]" << std::endl
261 << " Set value for the given path to the given type and value" << std::endl
262 << " where type is one of float/uint/int/bool/string. The type" << std::endl
263 << " is only necessary if you are creating a new value" << std::endl
264 << std::endl
265 << " set_default <path> <value> [type]" << std::endl
266 << " Set default value for the given path to the given type and value" << std::endl
267 << " where type is one of float/uint/int/bool/string. The type" << std::endl
268 << " is only necessary if you are creating a new value" << std::endl
269 << std::endl
270 << " set_comment <path> <comment>" << std::endl
271 << " Set comment for the given path to the given value. The value at" << std::endl
272 << " the given path must already exist in the host-specific configuration."
273 << std::endl
274 << std::endl
275 << " set_default_comment <path> <comment>" << std::endl
276 << " Set default comment for the given path to the given value. The value at"
277 << std::endl
278 << " the given path must already exist in the default configuration." << std::endl
279 << std::endl
280 << " erase <path>" << std::endl
281 << " Erase value for given path from config" << std::endl
282 << " erase_default <path>" << std::endl
283 << " Erase default value for given path from config" << std::endl
284 << std::endl
285 << "and options is none, one or more of the following:" << std::endl
286 << std::endl
287 << " -c Show comments (only available with list and watch cmd)" << std::endl
288 << " -a Show all values, even double if default and host-specific" << std::endl
289 << " values exist (only available with list and -r)" << std::endl
290 << " -q Quiet. Only show important output, suitable for parsing." << std::endl
291 << " (not supported for all commands yet) " << std::endl
292 << std::endl
293 << "You may use one of the following options where to retrieve the config from."
294 << std::endl
295 << "The default is '-r localhost'.\n"
296 << std::endl
297 << " -r host[:port] Remote host (and optionally port) to connect to" << std::endl
298 << " -f file Config file (relative to CONFDIR) to load" << std::endl
299 << std::endl;
300}
301
302/** Config tool main.
303 * @param argc argument count
304 * @param argv arguments
305 */
306int
307main(int argc, char **argv)
308{
309 ArgumentParser argp(argc, argv, "+hcar:qf:");
310
311 if (argp.has_arg("h")) {
312 print_usage(argv[0]);
313 exit(0);
314 }
315
316 std::string host = "localhost";
317 unsigned short int port = 1910;
318 if (argp.has_arg("r")) {
319 argp.parse_hostport("r", host, port);
320 }
321
322 std::string config_file;
323 if (argp.has_arg("f")) {
324 config_file = argp.arg("f");
325 }
326
327 bool quiet;
328 if (argp.has_arg("q")) {
329 quiet = true;
330 } else {
331 quiet = false;
332 }
333
334 FawkesNetworkClient * c = NULL;
335 NetworkConfiguration *netconf = NULL;
336 Configuration * config;
337
338 if (config_file.empty()) {
339 c = new FawkesNetworkClient(host.c_str(), port);
340 try {
341 c->connect();
342 } catch (Exception &e) {
343 printf("Could not connect to host: %s\n", host.c_str());
344 exit(1);
345 }
346
347 netconf = new NetworkConfiguration(c);
348 config = netconf;
349 } else {
350 if (fnmatch("*.sql", config_file.c_str(), FNM_PATHNAME) == 0) {
351 config = new SQLiteConfiguration(CONFDIR);
352 } else {
353 config = new YamlConfiguration(CONFDIR);
354 }
355
356 config->load(config_file.c_str());
357 }
358
359 const std::vector<const char *> &args = argp.items();
360
361 if (args.size() == 0) {
362 // show usage
363 printf("Not enough args\n\n");
364 print_usage(argv[0]);
365 } else if (strcmp("get", args[0]) == 0) {
366 if (args.size() == 2) {
367 if (!quiet) {
368 printf("Requesting value %s\n", args[1]);
369 }
370 Configuration::ValueIterator *i = config->get_value(args[1]);
371 if (i->next()) {
372 if (quiet) {
373 print_value(i);
374 } else {
375 print_header();
376 print_line(i);
377 }
378 } else {
379 if (!quiet) {
380 printf("No such value found!\n");
381 }
382 delete i;
383 return -2;
384 }
385 delete i;
386 } else {
387 // Error!
388 printf("You must supply path argument\n");
389 }
390 } else if ((strcmp("set", args[0]) == 0) || (strcmp("set_default", args[0]) == 0)) {
391 bool set_def = (strcmp("set_default", args[0]) == 0);
392 if (args.size() >= 3) {
393 // we have at least "set path value"
394 printf("Requesting old value for %s\n", args[1]);
395 Configuration::ValueIterator *i = config->get_value(args[1]);
396 print_header();
397 printf("OLD:\n");
398 if (i->next()) {
399 print_line(i);
400 } else {
401 printf("Value does not currently exist in configuration.\n");
402 }
403
404 std::string desired_type = "";
405 if (args.size() == 4) {
406 // we have "set path value type"
407 desired_type = args[3];
408 }
409
410 if ((desired_type == "") && !i->valid()) {
411 printf("Please specify type\n");
412 delete i;
413 } else if ((desired_type != "") && (i->valid() && (desired_type != i->type()))) {
414 printf("The given type '%s' contradicts with type '%s' in config. "
415 "Erase before setting with new type.\n",
416 desired_type.c_str(),
417 i->type());
418 delete i;
419 } else {
420 if (i->valid())
421 desired_type = i->type();
422
423 if (desired_type == "float") {
424 char *endptr;
425 float f = strtod(args[2], &endptr);
426 if (endptr[0] != 0) {
427 printf("ERROR: '%s' is not a float\n", args[2]);
428 } else {
429 if (!set_def) {
430 config->set_float(args[1], f);
431 } else {
432 config->set_default_float(args[1], f);
433 }
434 }
435 } else if ((desired_type == "unsigned int") || (desired_type == "uint")) {
436 char * endptr;
437 long int li = strtol(args[2], &endptr, 10);
438 if ((endptr[0] != 0) || (li < 0)) {
439 printf("ERROR: '%s' is not an unsigned int\n", args[2]);
440 } else {
441 if (!set_def) {
442 config->set_uint(args[1], li);
443 } else {
444 config->set_default_uint(args[1], li);
445 }
446 }
447 } else if (desired_type == "int") {
448 char * endptr;
449 long int li = strtol(args[2], &endptr, 10);
450 if (endptr[0] != 0) {
451 printf("ERROR: '%s' is not an int\n", args[2]);
452 } else {
453 if (!set_def) {
454 config->set_int(args[1], li);
455 } else {
456 config->set_default_int(args[1], li);
457 }
458 }
459 } else if (desired_type == "bool") {
460 bool valid = false;
461 bool b;
462 if (strcasecmp("true", args[2]) == 0) {
463 b = true;
464 valid = true;
465 } else if (strcasecmp("false", args[2]) == 0) {
466 b = false;
467 valid = true;
468 } else {
469 printf("ERROR: '%s' is not a boolean.\n", args[2]);
470 }
471 if (valid) {
472 if (!set_def) {
473 config->set_bool(args[1], b);
474 } else {
475 config->set_default_bool(args[1], b);
476 }
477 }
478 } else if (desired_type == "string") {
479 if (!set_def) {
480 config->set_string(args[1], args[2]);
481 } else {
482 config->set_default_string(args[1], args[2]);
483 }
484 } else {
485 printf("Invalid type: %s\n", desired_type.c_str());
486 }
487
488 delete i;
489
490 printf("NEW:\n");
491 i = config->get_value(args[1]);
492 if (i->next()) {
493 print_line(i);
494 } else {
495 printf("ERROR: value does not exist\n");
496 }
497 delete i;
498 }
499 } else {
500 printf("Usage: %s set <path> <value> [type]\n", argp.program_name());
501 }
502 } else if ((strcmp("set_comment", args[0]) == 0)
503 || (strcmp("set_default_comment", args[0]) == 0)) {
504 bool set_def = (strcmp("set_default_comment", args[0]) == 0);
505 if (args.size() >= 3) {
506 // we have at least "set_comment path value"
507
508 if (!set_def) {
509 config->set_comment(args[1], args[2]);
510 } else {
511 config->set_default_comment(args[1], args[2]);
512 }
513
514 } else {
515 printf("Usage: %s set_(default_)comment <path> <value>\n", argp.program_name());
516 }
517 } else if ((strcmp("erase", args[0]) == 0) || (strcmp("erase_default", args[0]) == 0)) {
518 bool erase_def = (strcmp("erase_default", args[0]) == 0);
519 if (args.size() == 2) {
520 printf("Erasing %svalue %s\n", (erase_def ? "default " : ""), args[1]);
521 bool found = false;
522 Configuration::ValueIterator *i = config->get_value(args[1]);
523 if (i->next()) {
524 print_header();
525 print_line(i);
526 found = true;
527 } else {
528 printf("No such value found!\n");
529 }
530 delete i;
531 if (found) {
532 if (erase_def) {
533 config->erase_default(args[1]);
534 } else {
535 config->erase(args[1]);
536 }
537 i = config->get_value(args[1]);
538 if (i->next()) {
539 printf("Failed to erase %s (default vs. non-default?)\n", args[1]);
540 } else {
541 printf("Successfully erased %s\n", args[1]);
542 }
543 delete i;
544 }
545 } else {
546 // Error!
547 printf("You must supply path argument\n");
548 }
549 } else if (strcmp("watch", args[0]) == 0) {
550 if (netconf) {
551 try {
552 netconf->set_mirror_mode(true);
553 } catch (Exception &e) {
554 e.print_trace();
555 return -1;
556 }
557 }
558 print_header();
559 config->lock();
561 while (i->next()) {
562 print_line(i, argp.has_arg("c"));
563 }
564 delete i;
565 config->unlock();
566 printf(
567 "------------------------------------------------------------------------------------\n");
568 printf("Modifications since watching:\n");
569 printf(
570 "------------------------------------------------------------------------------------\n");
571 ConfigChangeWatcherTool ccwt(config, c);
572 ccwt.run();
573 } else if (strcmp("list", args[0]) == 0) {
574 if (netconf) {
575 try {
576 printf("Transmitting config from host... ");
577 fflush(stdout);
578 netconf->set_mirror_mode(true);
579 printf("done\n");
580 } catch (Exception &e) {
581 printf("failed\n");
582 e.print_trace();
583 return -1;
584 }
585 }
586 config->lock();
587 print_header();
588 bool show_comments = argp.has_arg("c");
589 if (argp.has_arg("a") && netconf) {
590 printf("DEFAULT ENTRIES\n");
592 while (i->next()) {
593 print_line(i, show_comments);
594 }
595 delete i;
596 printf("HOST-SPECIFIC ENTRIES\n");
597 i = netconf->iterator_hostspecific();
598 while (i->next()) {
599 print_line(i, show_comments);
600 }
601 delete i;
602 } else {
604 while (i->next()) {
605 print_line(i, show_comments);
606 }
607 delete i;
608 }
609 config->unlock();
610 }
611
612 if (!quiet) {
613 printf("Cleaning up... ");
614 }
615 fflush(stdout);
616 delete config;
617 if (c) {
618 c->disconnect();
619 delete c;
620 }
621 if (!quiet) {
622 printf("done\n");
623 }
624
625 return 0;
626}
Tool to watch and output config changes.
Definition: main.cpp:43
virtual void config_value_changed(const Configuration::ValueIterator *v)
Called whenever a watched value has changed.
Definition: main.cpp:72
virtual void handle_signal(int signal)
Signal hanlding method.
Definition: main.cpp:59
virtual void config_tag_changed(const char *new_tag)
Called whenever the tag has changed.
Definition: main.cpp:66
ConfigChangeWatcherTool(Configuration *config, FawkesNetworkClient *c)
Constructor.
Definition: main.cpp:49
virtual void config_comment_changed(const Configuration::ValueIterator *v)
Called whenever a comment of a watched value has changed.
Definition: main.cpp:82
virtual void config_value_erased(const char *path)
Called whenever a value has been erased from the config.
Definition: main.cpp:88
void run()
Run.
Definition: main.cpp:97
Parse command line arguments.
Definition: argparser.h:64
Interface for configuration change handling.
Iterator interface to iterate over config values.
Definition: config.h:75
virtual bool is_list() const =0
Check if a value is a list.
virtual bool is_uint() const =0
Check if current value is a unsigned int.
virtual std::vector< unsigned int > get_uints() const =0
Get list of values from configuration which is of type unsigned int.
virtual const char * path() const =0
Path of value.
virtual bool get_bool() const =0
Get bool value.
virtual unsigned int get_uint() const =0
Get unsigned int value.
virtual bool next()=0
Check if there is another element and advance to this if possible.
virtual std::string get_as_string() const =0
Get value as string.
virtual std::vector< bool > get_bools() const =0
Get list of values from configuration which is of type bool.
virtual float get_float() const =0
Get float value.
virtual bool is_float() const =0
Check if current value is a float.
virtual bool is_int() const =0
Check if current value is a int.
virtual bool is_default() const =0
Check if current value was read from the default config.
virtual bool is_string() const =0
Check if current value is a string.
virtual bool is_bool() const =0
Check if current value is a bool.
virtual int get_int() const =0
Get int value.
virtual std::vector< std::string > get_strings() const =0
Get list of values from configuration which is of type string.
virtual bool valid() const =0
Check if the current element is valid.
virtual const char * type() const =0
Type of value.
virtual std::string get_comment() const =0
Get comment of value.
virtual std::string get_string() const =0
Get string value.
virtual std::vector< int > get_ints() const =0
Get list of values from configuration which is of type int.
virtual std::vector< float > get_floats() const =0
Get list of values from configuration which is of type float.
Interface for configuration handling.
Definition: config.h:68
virtual void set_comment(const char *path, const char *comment)=0
Set new comment for existing value.
virtual void set_uint(const char *path, unsigned int uint)=0
Set new value in configuration of type unsigned int.
virtual void set_bool(const char *path, bool b)=0
Set new value in configuration of type bool.
virtual ValueIterator * iterator()=0
Iterator for all values.
virtual void rem_change_handler(ConfigurationChangeHandler *h)
Remove a configuration change handler.
Definition: config.cpp:619
virtual void set_default_float(const char *path, float f)=0
Set new default value in configuration of type float.
virtual void set_float(const char *path, float f)=0
Set new value in configuration of type float.
virtual void set_default_string(const char *path, std::string &s)=0
Set new default value in configuration of type string.
virtual ValueIterator * get_value(const char *path)=0
Get value from configuration.
virtual void set_string(const char *path, std::string &s)=0
Set new value in configuration of type string.
virtual void erase_default(const char *path)=0
Erase the given default value from the configuration.
virtual void load(const char *file_path)=0
Load configuration.
virtual void lock()=0
Lock the config.
virtual void unlock()=0
Unlock the config.
virtual void set_default_bool(const char *path, bool b)=0
Set new default value in configuration of type bool.
virtual void set_int(const char *path, int i)=0
Set new value in configuration of type int.
virtual void set_default_uint(const char *path, unsigned int uint)=0
Set new default value in configuration of type unsigned int.
virtual void set_default_int(const char *path, int i)=0
Set new default value in configuration of type int.
virtual void set_default_comment(const char *path, const char *comment)=0
Set new default comment for existing default configuration value.
virtual void add_change_handler(ConfigurationChangeHandler *h)
Add a configuration change handler.
Definition: config.cpp:603
virtual void erase(const char *path)=0
Erase the given value from the configuration.
Base class for exceptions in Fawkes.
Definition: exception.h:36
void print_trace() noexcept
Prints trace to stderr.
Definition: exception.cpp:601
Simple Fawkes network client.
Definition: client.h:52
void wait(unsigned int component_id, unsigned int timeout_sec=15)
Wait for messages for component ID.
Definition: client.cpp:785
void connect()
Connect to remote.
Definition: client.cpp:424
void disconnect()
Disconnect socket.
Definition: client.cpp:539
Remote configuration via Fawkes net.
Definition: netconf.h:50
ValueIterator * iterator_default()
Iterator for all default values.
Definition: netconf.cpp:1357
ValueIterator * iterator_hostspecific()
Iterator for all host-specific values.
Definition: netconf.cpp:1374
virtual void set_mirror_mode(bool mirror)
Enable or disable mirror mode.
Definition: netconf.cpp:1269
Configuration storage using SQLite.
Definition: sqlite.h:41
Interface for signal handling.
Definition: signal.h:36
Configuration store using YAML documents.
Definition: yaml.h:43
Fawkes library namespace.