Fawkes API  Fawkes Development Version
main.cpp
00001 
00002 /***************************************************************************
00003  *  main.cpp - Fawkes config tool
00004  *
00005  *  Created: Mon Jan 08 16:43:45 2007
00006  *  Copyright  2006-2007  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU Library General Public License for more details.
00019  *
00020  *  Read the full text in the LICENSE.GPL file in the doc directory.
00021  */
00022 
00023 #include <netcomm/fawkes/client.h>
00024 #include <config/netconf.h>
00025 #include <config/change_handler.h>
00026 #include <utils/system/argparser.h>
00027 #include <utils/system/signal.h>
00028 
00029 #include <iostream>
00030 #include <cstring>
00031 #include <cstdlib>
00032 #include <cstdio>
00033 
00034 using namespace fawkes;
00035 
00036 /** Tool to watch and output config changes.
00037  */
00038 class ConfigChangeWatcherTool
00039   : public ConfigurationChangeHandler, public SignalHandler
00040 {
00041  public:
00042 
00043   /** Constructor.
00044    * @param config Configuration to watch
00045    * @param c network client, thread is cancelled on signal
00046    */
00047   ConfigChangeWatcherTool(Configuration *config, FawkesNetworkClient *c)
00048     : ConfigurationChangeHandler("")
00049   {
00050     this->c = c;
00051     this->config = config;
00052     quit = false;
00053     config->add_change_handler(this);
00054   }
00055 
00056   virtual void handle_signal(int signal)
00057   {
00058     config->rem_change_handler(this);
00059     quit = true;
00060   }
00061 
00062   virtual void config_tag_changed(const char *new_tag)
00063   {
00064     printf("--> New tag loaded: %s\n", new_tag);
00065   }
00066 
00067   virtual void config_value_changed(const Configuration::ValueIterator *v)
00068   {
00069     printf("%s %-55s| %-8s| %-14s\n", v->is_default() ? "*" : " ",
00070            v->path(), v->type(), v->get_as_string().c_str());
00071   }
00072 
00073   virtual void config_comment_changed(const Configuration::ValueIterator *v)
00074   {
00075     printf("%s %s: %s\n", v->is_default() ? "C" : "c",
00076            v->path(), v->get_comment().c_str());
00077   }
00078 
00079   virtual void config_value_erased(const char *path)
00080   {
00081     printf("  %-55s| %-8s| %-14s\n", path, "", "ERASED");
00082   }
00083 
00084 
00085   /** Run.
00086    * This joins the network thread.
00087    */
00088   void
00089   run()
00090   {
00091     while ( ! quit ) {
00092       c->wait(FAWKES_CID_CONFIGMANAGER);
00093     }
00094   }
00095 
00096  private:
00097   FawkesNetworkClient *c;
00098   Configuration *config;
00099   bool quit;
00100 
00101 };
00102 
00103 
00104 /** Print header. */
00105 void
00106 print_header()
00107 {
00108   printf("D %-55s| %-8s| %-14s\n", "Path", "Type", "Value");
00109   printf("--------------------------------------------------------------------------------------\n");
00110 }
00111 
00112 /** Print a single value.
00113  * @param i config item to print.
00114  */
00115 void
00116 print_line(Configuration::ValueIterator *i, bool show_comment = false)
00117 {
00118   if ( i->is_float() ) {
00119     printf("%s %-55s| %-8s| %-14f\n", (i->is_default() ? "*" : " "), i->path(), i->type(), i->get_float());
00120   } else if ( i->is_uint() ) {
00121     printf("%s %-55s| %-8s| %-14u\n", (i->is_default() ? "*" : " "), i->path(), "uint", i->get_uint());
00122   } else if ( i->is_int() ) {
00123     printf("%s %-55s| %-8s| %-14i\n", (i->is_default() ? "*" : " "), i->path(), i->type(), i->get_int());
00124   } else if ( i->is_bool() ) {
00125     printf("%s %-55s| %-8s| %-14s\n", (i->is_default() ? "*" : " "), i->path(), i->type(), (i->get_bool() ? "true" : "false"));
00126   } else if ( i->is_string() ) {
00127     printf("%s %-55s| %-8s| %-14s\n", (i->is_default() ? "*" : " "), i->path(), i->type(), i->get_string().c_str());
00128   }
00129 
00130   if (show_comment) {
00131     try {
00132       std::string comment = i->get_comment();
00133       if (comment != "") {
00134         printf("C %-55s: %s\n", i->path(), comment.c_str());
00135       }
00136     } catch (Exception &e) {
00137       // maybe there is no comment, ignore it...
00138     }
00139   }
00140 }
00141 
00142 /** Print a line of output.
00143  * @param i config item to print.
00144  */
00145 void
00146 print_value(Configuration::ValueIterator *i, bool show_comment = false)
00147 {
00148   if ( i->is_float() ) {
00149     printf("%-14f\n", i->get_float());
00150   } else if ( i->is_uint() ) {
00151     printf("%-14u\n", i->get_uint());
00152   } else if ( i->is_int() ) {
00153     printf("%-14i\n", i->get_int());
00154   } else if ( i->is_bool() ) {
00155     printf("%-14s\n", (i->get_bool() ? "true" : "false"));
00156   } else if ( i->is_string() ) {
00157     printf("%-14s\n", i->get_string().c_str());
00158   }
00159 }
00160 
00161 
00162 void
00163 print_usage(const char *program_name)
00164 {
00165   std::cout << "Usage: " << program_name << " [options] <cmd>" << std::endl
00166             << "where cmd is one of the following:" << std::endl << std::endl
00167             << "  list" << std::endl
00168             << "    List all configuration items" << std::endl << std::endl
00169             << "  watch" << std::endl
00170             << "    Watch configuration changes" << std::endl << std::endl
00171             << "  get <path>" << std::endl
00172             << "    Get value for the given path" << std::endl << std::endl
00173             << "  set <path> <value> [type]" << std::endl
00174             << "    Set value for the given path to the given type and value" << std::endl
00175             << "    where type is one of float/uint/int/bool/string. The type" << std::endl
00176             << "    is only necessary if you are creating a new value" << std::endl << std::endl
00177             << "  set_default <path> <value> [type]" << std::endl
00178             << "    Set default value for the given path to the given type and value" << std::endl
00179             << "    where type is one of float/uint/int/bool/string. The type" << std::endl
00180             << "    is only necessary if you are creating a new value" << std::endl << std::endl
00181             << "  set_comment <path> <comment>" << std::endl
00182             << "    Set comment for the given path to the given value. The value at" << std::endl
00183             << "    the given path must already exist in the host-specific configuration." << std::endl << std::endl
00184             << "  set_default_comment <path> <comment>" << std::endl
00185             << "    Set default comment for the given path to the given value. The value at" << std::endl
00186             << "    the given path must already exist in the default configuration." << std::endl << std::endl
00187             << "  erase <path>" << std::endl
00188             << "    Erase value for given path from config" << std::endl
00189             << "  erase_default <path>" << std::endl
00190             << "    Erase default value for given path from config" << std::endl << std::endl
00191             << "and options is none, one or more of the following:" << std::endl << std::endl
00192             << "  -c   Show comments (only available with list and watch cmd)" << std::endl
00193             << "  -a   Show all values, even double if default and host-specific " << std::endl
00194             << "       values exist (only available with list)" << std::endl
00195             << "  -q   Quiet. Only show important output, suitable for parsing. " << std::endl
00196             << "       (not supported for all commands yet) " << std::endl
00197             << "  -r host[:port]  Remote host (and optionally port) to connect to\n" << std::endl
00198             << std::endl;
00199 }
00200 
00201 /** Config tool main.
00202  * @param argc argument count
00203  * @param argv arguments
00204  */
00205 int
00206 main(int argc, char **argv)
00207 {
00208   ArgumentParser argp(argc, argv, "+hcar:q");
00209 
00210   if ( argp.has_arg("h") ) {
00211     print_usage(argv[0]);
00212     exit(0);
00213   }
00214 
00215   std::string host = "localhost";
00216   unsigned short int port = 1910;
00217   if ( argp.has_arg("r") ) {
00218     argp.parse_hostport("r", host, port);
00219   }
00220 
00221   bool quiet;
00222   if ( argp.has_arg("q") ) {
00223     quiet = true;
00224   } else {
00225     quiet = false;
00226   }
00227 
00228   FawkesNetworkClient *c = new FawkesNetworkClient(host.c_str(), port);
00229   try {
00230     c->connect();
00231   } catch( Exception &e ) {
00232     printf("Could not connect to host: %s\n", host.c_str());
00233     exit(1);
00234   }
00235 
00236   NetworkConfiguration *netconf = new NetworkConfiguration(c);
00237 
00238   const std::vector< const char* > & args = argp.items();
00239 
00240   if ( args.size() == 0) {
00241     // show usage
00242     printf("Not enough args\n\n");
00243     print_usage(argv[0]);
00244   } else if (strcmp("get", args[0]) == 0) {
00245     if (args.size() == 2) {
00246       if( ! quiet ) {
00247         printf("Requesting value %s\n", args[1]);
00248       }
00249       Configuration::ValueIterator *i = netconf->get_value(args[1]);
00250       if ( i->next() ) {
00251         if( quiet ) {
00252           print_value(i);
00253         } else {
00254           print_header();
00255           print_line(i);
00256         }
00257       } else {
00258         printf("No such value found!\n");
00259       }
00260       delete i;
00261     } else {
00262       // Error!
00263       printf("You must supply path argument\n");
00264     }
00265   } else if ((strcmp("set", args[0]) == 0) || (strcmp("set_default", args[0]) == 0)) {
00266     bool set_def = (strcmp("set_default", args[0]) == 0);
00267     if (args.size() >= 3) {
00268       // we have at least "set path value"
00269       printf("Requesting old value for %s\n", args[1]);
00270       Configuration::ValueIterator *i = netconf->get_value(args[1]);
00271       print_header();
00272       printf("OLD:\n");
00273       if ( i->next() ) {
00274         print_line(i);
00275       } else {
00276         printf("Value does not currently exist in configuration.\n");
00277       }
00278 
00279       std::string desired_type = "";
00280       if (args.size() == 4) {
00281         // we have "set path value type"
00282         desired_type = args[3];
00283       }
00284 
00285       if ( (desired_type == "") && ! i->valid()) {
00286         printf("Please specify type\n");
00287         delete i;
00288       } else if ( (desired_type != "") && (i->valid() && (desired_type != i->type())) ) {
00289         printf("The given type '%s' contradicts with type '%s' in config. "
00290                "Erase before setting with new type.\n", desired_type.c_str(), i->type());
00291         delete i;
00292       } else {
00293         if ( i->valid() ) desired_type = i->type();
00294 
00295         if ( desired_type == "float" ) {
00296           char *endptr;
00297           float f = strtod(args[2], &endptr);
00298           if ( endptr[0] != 0 ) {
00299             printf("ERROR: '%s' is not a float\n", args[2]);
00300           } else {
00301             if ( ! set_def ) {
00302               netconf->set_float(args[1], f);
00303             } else {
00304               netconf->set_default_float(args[1], f);
00305             }
00306           }
00307         } else if ( (desired_type == "unsigned int") || (desired_type == "uint") ) {
00308           char *endptr;
00309           long int li = strtol(args[2], &endptr, 10);
00310           if ( (endptr[0] != 0) || (li < 0) ) {
00311             printf("ERROR: '%s' is not an unsigned int\n", args[2]);
00312           } else {
00313             if ( ! set_def ) {
00314               netconf->set_uint(args[1], li);
00315             } else {
00316               netconf->set_default_uint(args[1], li);
00317             }
00318           }
00319         } else if ( desired_type == "int" ) {
00320           char *endptr;
00321           long int li = strtol(args[2], &endptr, 10);
00322           if ( endptr[0] != 0 ) {
00323             printf("ERROR: '%s' is not an int\n", args[2]);
00324           } else {
00325             if ( ! set_def ) {
00326               netconf->set_int(args[1], li);
00327             } else {
00328               netconf->set_default_int(args[1], li);
00329             }
00330           }
00331         } else if ( desired_type == "bool" ) {
00332           bool valid = false;
00333           bool b;
00334           if ( strcasecmp("true", args[2]) == 0 ) {
00335             b = true;
00336             valid = true;
00337           } else if ( strcasecmp("false", args[2]) == 0 ) {
00338             b = false;
00339             valid = true;
00340           } else {
00341             printf("ERROR: '%s' is not a boolean.\n", args[2]);
00342           }
00343           if (valid) {
00344             if ( ! set_def ) {
00345               netconf->set_bool(args[1], b);
00346             } else {
00347               netconf->set_default_bool(args[1], b);
00348             }
00349           }
00350         } else if ( desired_type == "string" ) {
00351           if ( ! set_def ) {
00352             netconf->set_string(args[1], args[2]);
00353           } else {
00354             netconf->set_default_string(args[1], args[2]);
00355           }
00356         } else {
00357           printf("Invalid type: %s\n", desired_type.c_str());
00358         }
00359 
00360         delete i;
00361 
00362         printf("NEW:\n");
00363         i = netconf->get_value(args[1]);
00364         if ( i->next() ) {
00365           print_line(i);
00366         } else {
00367           printf("ERROR: value does not exist\n");
00368         }
00369         delete i;
00370 
00371       }
00372     } else {
00373       printf("Usage: %s set <path> <value> [type]\n", argp.program_name());
00374     }
00375   } else if ((strcmp("set_comment", args[0]) == 0) ||
00376              (strcmp("set_default_comment", args[0]) == 0)) {
00377     bool set_def = (strcmp("set_default_comment", args[0]) == 0);
00378     if (args.size() >= 3) {
00379       // we have at least "set_comment path value"
00380 
00381       if ( ! set_def ) {
00382         netconf->set_comment(args[1], args[2]);
00383       } else {
00384         netconf->set_default_comment(args[1], args[2]);
00385       }
00386 
00387     } else {
00388       printf("Usage: %s set_(default_)comment <path> <value>\n", argp.program_name());
00389     }
00390   } else if ((strcmp("erase", args[0]) == 0) || (strcmp("erase_default", args[0]) == 0)) {
00391     bool erase_def = (strcmp("erase_default", args[0]) == 0);
00392     if (args.size() == 2) {
00393       printf("Erasing %svalue %s\n", (erase_def ? "default " : ""), args[1]);
00394       bool found = false;
00395       Configuration::ValueIterator *i = netconf->get_value(args[1]);
00396       if ( i->next() ) {
00397         print_header();
00398         print_line(i);
00399         found = true;
00400       } else {
00401         printf("No such value found!\n");
00402       }
00403       delete i;
00404       if ( found ) {
00405         if ( erase_def ) {
00406           netconf->erase_default(args[1]);
00407         } else {
00408           netconf->erase(args[1]);
00409         }
00410         i = netconf->get_value(args[1]);
00411         if ( i->next() ) {
00412           printf("Failed to erase %s (default vs. non-default?)\n", args[1]);
00413         } else {
00414           printf("Successfully erased %s\n", args[1]);
00415         }
00416         delete i;
00417       }
00418     } else {
00419       // Error!
00420       printf("You must supply path argument\n");
00421     }
00422   } else if (strcmp("watch", args[0]) == 0) {
00423     try {
00424       netconf->set_mirror_mode(true);
00425     } catch (Exception &e) {
00426       e.print_trace();
00427       return -1;
00428     }
00429     print_header();
00430     netconf->lock();
00431     Configuration::ValueIterator *i = netconf->iterator();
00432     while ( i->next() ) {
00433       print_line(i, argp.has_arg("c"));
00434     }
00435     delete i;
00436     netconf->unlock();
00437     printf("------------------------------------------------------------------------------------\n");
00438     printf("Modifications since watching:\n");
00439     printf("------------------------------------------------------------------------------------\n");
00440     ConfigChangeWatcherTool ccwt(netconf, c);
00441     ccwt.run();
00442   } else if (strcmp("list", args[0]) == 0) {
00443     printf("Transmitting config from host... ");
00444     fflush(stdout);
00445     try {
00446       netconf->set_mirror_mode(true);
00447     } catch (Exception &e) {
00448       e.print_trace();
00449       return -1;
00450     }
00451     netconf->lock();
00452     printf("done\n");
00453     print_header();
00454     bool show_comments = argp.has_arg("c");
00455     if (argp.has_arg("a")) {
00456       printf("DEFAULT ENTRIES\n");
00457       Configuration::ValueIterator *i = netconf->iterator_default();
00458       while ( i->next() ) {
00459         print_line(i, show_comments);
00460       }
00461       delete i;
00462       printf("HOST-SPECIFIC ENTRIES\n");
00463       i = netconf->iterator_hostspecific();
00464       while ( i->next() ) {
00465         print_line(i, show_comments);
00466       }
00467       delete i;
00468     } else {
00469       Configuration::ValueIterator *i = netconf->iterator();
00470       while ( i->next() ) {
00471         print_line(i, show_comments);
00472       }
00473       delete i;
00474     }
00475     netconf->unlock();
00476   }
00477 
00478   if( ! quiet ) {
00479     printf("Cleaning up... ");
00480   }
00481   fflush(stdout);
00482   delete netconf;
00483   c->disconnect();
00484 
00485   delete c;
00486   if( ! quiet ) {
00487     printf("done\n");
00488   }
00489 
00490   return 0;
00491 }