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 
38 using namespace fawkes;
39 
40 /** Tool to watch and output config changes.
41  */
43 {
44 public:
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
97  run()
98  {
99  while (!quit) {
100  if (c) {
101  c->wait(FAWKES_CID_CONFIGMANAGER);
102  } else {
103  usleep(500000);
104  }
105  }
106  }
107 
108 private:
110  Configuration * config;
111  bool quit;
112 };
113 
114 /** Print header. */
115 void
116 print_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  */
126 void
127 print_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  */
210 void
211 print_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 
245 void
246 print_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  */
306 int
307 main(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();
560  Configuration::ValueIterator *i = config->iterator();
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 {
603  Configuration::ValueIterator *i = config->iterator();
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 }
virtual std::string get_comment() const =0
Get comment of value.
virtual ValueIterator * iterator()=0
Iterator for all values.
virtual void load(const char *file_path)=0
Load configuration.
virtual void set_default_float(const char *path, float f)=0
Set new default value in configuration of type float.
Simple Fawkes network client.
Definition: client.h:51
virtual std::vector< bool > get_bools() const =0
Get list of values from configuration which is of type bool.
virtual void set_default_int(const char *path, int i)=0
Set new default value in configuration of type int.
void run()
Run.
Definition: main.cpp:97
virtual std::vector< std::string > get_strings() const =0
Get list of values from configuration which is of type string.
virtual void set_default_comment(const char *path, const char *comment)=0
Set new default comment for existing default configuration value.
virtual std::vector< float > get_floats() const =0
Get list of values from configuration which is of type float.
virtual const char * type() const =0
Type of value.
Configuration storage using SQLite.
Definition: sqlite.h:40
virtual bool is_bool() const =0
Check if current value is a bool.
Fawkes library namespace.
virtual void handle_signal(int signal)
Signal hanlding method.
Definition: main.cpp:59
void disconnect()
Disconnect socket.
Definition: client.cpp:539
virtual void config_tag_changed(const char *new_tag)
Called whenever the tag has changed.
Definition: main.cpp:66
Interface for configuration change handling.
void connect()
Connect to remote.
Definition: client.cpp:424
Interface for signal handling.
Definition: signal.h:35
Parse command line arguments.
Definition: argparser.h:63
virtual bool next()=0
Check if there is another element and advance to this if possible.
virtual void config_comment_changed(const Configuration::ValueIterator *v)
Called whenever a comment of a watched value has changed.
Definition: main.cpp:82
virtual std::vector< int > get_ints() const =0
Get list of values from configuration which is of type int.
virtual float get_float() const =0
Get float value.
virtual void set_int(const char *path, int i)=0
Set new value in configuration of type int.
virtual unsigned int get_uint() const =0
Get unsigned int value.
ConfigChangeWatcherTool(Configuration *config, FawkesNetworkClient *c)
Constructor.
Definition: main.cpp:49
virtual bool is_float() const =0
Check if current value is a float.
virtual void set_bool(const char *path, bool b)=0
Set new value in configuration of type bool.
virtual bool is_int() const =0
Check if current value is a int.
ValueIterator * iterator_default()
Iterator for all default values.
Definition: netconf.cpp:1357
virtual bool get_bool() const =0
Get bool value.
virtual void config_value_erased(const char *path)
Called whenever a value has been erased from the config.
Definition: main.cpp:88
virtual void set_float(const char *path, float f)=0
Set new value in configuration of type float.
virtual int get_int() const =0
Get int value.
virtual std::string get_as_string() const =0
Get value as string.
virtual bool is_string() const =0
Check if current value is a string.
virtual void erase_default(const char *path)=0
Erase the given default value from the configuration.
virtual void erase(const char *path)=0
Erase the given value from the configuration.
virtual void set_default_bool(const char *path, bool b)=0
Set new default value in configuration of type bool.
virtual std::vector< unsigned int > get_uints() const =0
Get list of values from configuration which is of type unsigned int.
Configuration store using YAML documents.
Definition: yaml.h:42
Base class for exceptions in Fawkes.
Definition: exception.h:35
virtual void set_default_uint(const char *path, unsigned int uint)=0
Set new default value in configuration of type unsigned int.
virtual void config_value_changed(const Configuration::ValueIterator *v)
Called whenever a watched value has changed.
Definition: main.cpp:72
virtual void rem_change_handler(ConfigurationChangeHandler *h)
Remove a configuration change handler.
Definition: config.cpp:619
virtual bool is_uint() const =0
Check if current value is a unsigned int.
virtual bool is_list() const =0
Check if a value is a list.
virtual std::string get_string() const =0
Get string value.
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
virtual const char * path() const =0
Path of value.
virtual bool valid() const =0
Check if the current element is valid.
void print_trace()
Prints trace to stderr.
Definition: exception.cpp:601
virtual void unlock()=0
Unlock the config.
virtual ValueIterator * get_value(const char *path)=0
Get value from configuration.
Iterator interface to iterate over config values.
Definition: config.h:71
virtual bool is_default() const =0
Check if current value was read from the default config.
virtual void add_change_handler(ConfigurationChangeHandler *h)
Add a configuration change handler.
Definition: config.cpp:603
virtual void set_default_string(const char *path, std::string &s)=0
Set new default value in configuration of type string.
Interface for configuration handling.
Definition: config.h:64
virtual void set_comment(const char *path, const char *comment)=0
Set new comment for existing value.
Tool to watch and output config changes.
Definition: main.cpp:42
virtual void set_uint(const char *path, unsigned int uint)=0
Set new value in configuration of type unsigned int.
virtual void set_string(const char *path, std::string &s)=0
Set new value in configuration of type string.
virtual void lock()=0
Lock the config.
Remote configuration via Fawkes net.
Definition: netconf.h:49