Fawkes API  Fawkes Development Version
run.cpp
00001 
00002 /***************************************************************************
00003  *  run.cpp - Fawkes run functions
00004  *
00005  *  Created: Wed May 04 23:23:23 2011
00006  *  Copyright  2006-2011  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. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023 
00024 #include <baseapp/run.h>
00025 #include <baseapp/daemonize.h>
00026 #include <baseapp/main_thread.h>
00027 #include <baseapp/thread_manager.h>
00028 
00029 #include <core/threading/thread.h>
00030 
00031 #include <blackboard/local.h>
00032 #include <config/sqlite.h>
00033 #include <config/net_handler.h>
00034 #include <utils/ipc/shm.h>
00035 #include <utils/system/argparser.h>
00036 #include <logging/multi.h>
00037 #include <logging/console.h>
00038 #include <logging/liblogger.h>
00039 #include <logging/factory.h>
00040 #include <logging/network.h>
00041 #include <utils/time/clock.h>
00042 #include <netcomm/fawkes/network_manager.h>
00043 #include <plugin/manager.h>
00044 #include <plugin/net/handler.h>
00045 #include <aspect/manager.h>
00046 
00047 
00048 #include <sys/types.h>
00049 #include <sys/stat.h>
00050 #include <pwd.h>
00051 #include <grp.h>
00052 #include <cstdlib>
00053 #include <cstdio>
00054 #include <cstring>
00055 #include <unistd.h>
00056 #include <signal.h>
00057 
00058 namespace fawkes {
00059   namespace runtime {
00060 #if 0 /* just to make Emacs auto-indent happy */
00061   }
00062 }
00063 #endif
00064 
00065 ArgumentParser            * argument_parser = NULL;
00066 FawkesMainThread          * main_thread = NULL;
00067 MultiLogger               * logger = NULL;
00068 NetworkLogger             * network_logger = NULL;
00069 BlackBoard                * blackboard = NULL;
00070 SQLiteConfiguration       * config = NULL;
00071 PluginManager             * plugin_manager = NULL;
00072 AspectManager             * aspect_manager = NULL;
00073 ThreadManager             * thread_manager = NULL;
00074 FawkesNetworkManager      * network_manager = NULL;
00075 ConfigNetworkHandler      * nethandler_config = NULL;
00076 PluginNetworkHandler      * nethandler_plugin = NULL;
00077 Clock                     * clock = NULL;
00078 SharedMemoryRegistry      * shm_registry;
00079 InitOptions               * init_options = NULL;
00080 
00081 // this is NOT shared to the outside
00082 FawkesMainThread::Runner  * runner = NULL;
00083 
00084 int
00085 init(int argc, char **argv)
00086 {
00087   return init(InitOptions(argc, argv));
00088 }
00089 
00090 
00091 int
00092 init(InitOptions options)
00093 {
00094   init_options = new InitOptions(options);
00095 
00096   if (init_options->show_help())  return 0;
00097 
00098   if ( options.daemonize() ) {
00099     fawkes::daemon::init(options.daemon_pid_file(), options.basename());
00100     if (options.daemonize_kill()) {
00101       fawkes::daemon::kill();
00102     } else if (options.daemonize_status()) {
00103       return fawkes::daemon::running() ? 0 : 1;
00104     } else {
00105       fawkes::daemon::start();
00106     }
00107   }
00108 
00109   // *** set user group if requested
00110   const char *user  = NULL;
00111   const char *group = NULL;
00112   if (options.has_username()) {
00113     user = options.username();
00114   }
00115   if (options.has_groupname()) {
00116     group = options.groupname();
00117   }
00118 
00119   if (user != NULL) {
00120     struct passwd *pw;
00121     if (! (pw = getpwnam(user))) {
00122       printf("Failed to find user %s, check -u argument.\n", user);
00123       return 203;
00124     }
00125     int r = 0;
00126     r = setreuid(pw->pw_uid, pw->pw_uid);
00127     if (r < 0) {
00128       perror("Failed to drop privileges (user)");
00129     }
00130   }
00131 
00132   if (group != NULL) {
00133     struct group *gr;
00134     if (! (gr = getgrnam(group))) {
00135       printf("Failed to find group %s, check -g argument.\n", user);
00136       return 204;
00137     }
00138     int r = 0;
00139     r = setregid(gr->gr_gid, gr->gr_gid);
00140     if (r < 0) {
00141       perror("Failed to drop privileges (group)");
00142     }
00143   }
00144 
00145   // *** setup base thread and shm registry
00146   Thread::init_main();
00147   shm_registry = new SharedMemoryRegistry(true);
00148 
00149   // *** setup logging
00150   if (options.has_loggers()) {
00151     try {
00152       logger = LoggerFactory::multilogger_instance(options.loggers());
00153     } catch (Exception &e) {
00154       e.append("Initializing multi logger failed");
00155       throw;
00156     }
00157   } else {
00158     logger = new MultiLogger(new ConsoleLogger());
00159   }
00160 
00161   logger->set_loglevel(options.log_level());
00162   LibLogger::init(logger);
00163 
00164   // *** Prepare home dir directory, just in case
00165   const char *homedir = getenv("HOME");
00166   if (homedir) {
00167     char *userdir;
00168     if (asprintf(&userdir, "%s/%s", homedir, USERDIR) != -1) {
00169       if (access(userdir, W_OK) != 0) {
00170         if (mkdir(userdir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1) {
00171           logger->log_warn("FawkesMainThread", "Failed to create .fawkes "
00172                            "directory %s, trying without", userdir);
00173         }
00174       }
00175       free(userdir);
00176     }
00177   }
00178 
00179   // *** setup config
00180   config = new SQLiteConfiguration(CONFDIR);
00181 
00182   config->load(options.host_config(), options.default_config());
00183 
00184   try {
00185     SQLiteConfiguration::SQLiteValueIterator *i = config->modified_iterator();
00186     while (i->next()) {
00187       std::string modtype = i->get_modtype();
00188       if (modtype == "changed") {
00189         logger->log_warn("FawkesMainThread", "Default config value CHANGED: %s"
00190                          "(was: %s now: %s)", i->path(),
00191                          i->get_oldvalue().c_str(), i->get_as_string().c_str());
00192       } else if (modtype == "erased") {
00193         logger->log_warn("FawkesMainThread", "Default config value ERASED:  %s",
00194                          i->path());
00195       } else {
00196         logger->log_debug("FawkesMainThread", "Default config value ADDED:   %s "
00197                           "(value: %s)", i->path(), i->get_as_string().c_str());
00198       }
00199     }
00200     delete i;
00201   } catch (Exception &e) {
00202     logger->log_warn("FawkesMainThread", "Failed to read modified default "
00203                      "config values, no dump?");
00204   }
00205 
00206 
00207   // *** Determine network parameters
00208   unsigned int net_tcp_port     = 1910;
00209   std::string  net_service_name = "Fawkes on %h";
00210   if (options.has_net_tcp_port()) {
00211     net_tcp_port = options.net_tcp_port();
00212   } else {
00213     try {
00214       net_tcp_port = config->get_uint("/fawkes/mainapp/net/tcp_port");
00215     } catch (Exception &e) {}  // ignore, we stick with the default
00216   }
00217 
00218   if (options.has_net_service_name()) {
00219     net_service_name = options.net_service_name();
00220   } else {
00221     try {
00222       net_service_name = config->get_string("/fawkes/mainapp/net/service_name");
00223     } catch (Exception &e) {}  // ignore, we stick with the default
00224   }
00225 
00226   if (net_tcp_port > 65535) {
00227     logger->log_warn("FawkesMainThread", "Invalid port '%u', using 1910",
00228                      net_tcp_port);
00229     net_tcp_port = 1910;
00230   }
00231 
00232   // *** Setup blackboard
00233   std::string bb_magic_token = "";
00234   unsigned int bb_size = 2097152;
00235   try {
00236     bb_magic_token = config->get_string("/fawkes/mainapp/blackboard_magic_token");
00237     logger->log_info("FawkesMainApp", "BlackBoard magic token defined. "
00238                      "Using shared memory BlackBoard.");
00239   } catch (Exception &e) {
00240     // ignore
00241   }
00242   try {
00243     bb_size = config->get_uint("/fawkes/mainapp/blackboard_size");
00244   } catch (Exception &e) {
00245     logger->log_warn("FawkesMainApp", "BlackBoard size not defined. "
00246                      "Will use %u, saving to default DB", bb_size);
00247     config->set_default_uint("/fawkes/mainapp/blackboard_size", bb_size);
00248   }
00249 
00250   // Cleanup stale BlackBoard shared memory segments if requested
00251   if ( options.bb_cleanup()) {
00252     LocalBlackBoard::cleanup(bb_magic_token.c_str(),
00253                              /* output with lister? */ true);
00254     SharedMemoryRegistry::cleanup();
00255   }
00256 
00257   LocalBlackBoard *lbb = NULL;
00258   if ( bb_magic_token == "") {
00259     lbb = new LocalBlackBoard(bb_size);
00260   } else {
00261     lbb = new LocalBlackBoard(bb_size, bb_magic_token.c_str());
00262   }
00263   blackboard = lbb;
00264 
00265   aspect_manager     = new AspectManager();
00266   thread_manager     = new ThreadManager(aspect_manager, aspect_manager);
00267 
00268   plugin_manager     = new PluginManager(thread_manager, config,
00269                                          "/fawkes/meta_plugins/",
00270                                          options.plugin_module_flags(),
00271                                          options.init_plugin_cache());
00272   network_manager    = new FawkesNetworkManager(thread_manager,
00273                                                 net_tcp_port,
00274                                                 net_service_name.c_str());
00275   nethandler_config  = new ConfigNetworkHandler(config,
00276                                                 network_manager->hub());
00277 
00278   nethandler_plugin  = new PluginNetworkHandler(plugin_manager,
00279                                                 network_manager->hub());
00280   nethandler_plugin->start();
00281 
00282   network_logger = new NetworkLogger(network_manager->hub(),
00283                                      logger->loglevel());
00284   logger->add_logger(network_logger);
00285 
00286   clock = Clock::instance();
00287 
00288   lbb->start_nethandler(network_manager->hub());
00289 
00290 
00291   // *** Create main thread, but do not start, yet
00292   main_thread = new fawkes::FawkesMainThread(config, logger,
00293                                              thread_manager,
00294                                              plugin_manager,
00295                                              options.load_plugin_list(),
00296                                              options.default_plugin());
00297 
00298   aspect_manager->register_default_inifins(blackboard,
00299                                            thread_manager->aspect_collector(),
00300                                            config, logger, clock,
00301                                            network_manager->hub(),
00302                                            main_thread, logger,
00303                                            thread_manager,
00304                                            network_manager->nnresolver(),
00305                                            network_manager->service_publisher(),
00306                                            network_manager->service_browser(),
00307                                            plugin_manager);
00308 
00309   
00310 
00311   return 0;
00312 }
00313 
00314 void
00315 cleanup()
00316 {
00317   if (init_options->daemonize()) {
00318     fawkes::daemon::cleanup();
00319   }
00320 
00321   if (nethandler_plugin) {
00322     nethandler_plugin->cancel();
00323     nethandler_plugin->join();
00324   }
00325 
00326   if (logger) {
00327     // Must delete network logger first since network manager
00328     // has to die before the LibLogger is finalized.
00329     logger->remove_logger(network_logger);
00330     delete network_logger;
00331   }
00332 
00333   delete main_thread;
00334   delete argument_parser;
00335   delete init_options;
00336   delete nethandler_config;
00337   delete nethandler_plugin;
00338   delete plugin_manager;
00339   delete network_manager;
00340   delete config;
00341   delete thread_manager;
00342   delete aspect_manager;
00343   delete shm_registry;
00344 
00345   main_thread = NULL;
00346   argument_parser = NULL;
00347   init_options = NULL;
00348   nethandler_config = NULL;
00349   nethandler_plugin = NULL;
00350   plugin_manager = NULL;
00351   network_manager = NULL;
00352   config = NULL;
00353   thread_manager = NULL;
00354   aspect_manager = NULL;
00355   shm_registry = NULL;
00356 
00357   // implicitly frees multi_logger and all sub-loggers
00358   LibLogger::finalize();
00359   logger = NULL;
00360 
00361   Clock::finalize();
00362   clock = NULL;
00363 
00364   try {
00365     Thread::destroy_main();
00366   } catch (Exception &e) {} // ignored, can fire on show_help
00367 
00368   // should be last, because of not disabled this hosts the
00369   // default signal handlers
00370   delete runner;
00371   runner = 0;
00372 }
00373 
00374 void
00375 run()
00376 {
00377   if (init_options->show_help()) {
00378     print_usage(init_options->basename());
00379     return;
00380   }
00381 
00382   bool defsigs = init_options->default_signal_handlers();
00383   runner = new FawkesMainThread::Runner(main_thread, defsigs);
00384 
00385   try {
00386     runner->run();
00387   } catch (Exception &e) {
00388     printf("Running Fawkes failed\n");
00389     e.print_trace();
00390   }
00391 }
00392 
00393 
00394 /** Quit Fawkes.
00395  * You can call this from within Fawkes to quit Fawkes. Use with extreme care an
00396  * only rarely.
00397  * This sends SIGINT to the local process. This triggers the quit routine but also
00398  * takes a currently running init into account. This is prone to the same potential
00399  * problems as a SIGINT received otherwise, e.g. a never-ending thread blocking
00400  * the main thread from cancelling.
00401  */
00402 void
00403 quit()
00404 {
00405   kill(getpid(), SIGINT);
00406 }
00407 
00408 void
00409 print_usage(const char *progname)
00410 {
00411   printf("Fawkes Main Application - Usage Instructions\n"
00412          "================================================"
00413          "===============================\n"
00414          "Usage: %s [options]\n"
00415          "where [options] is one or more of:\n"
00416          " -h                       These help instructions\n"
00417          " -C                       Cleanup old BB and shared memory segments\n"
00418          " -c db-file               Mutable configuration file, created if it "
00419          "does not\n                          "
00420          "exist, if it does must contain valid SQLite database\n"
00421          " -d sql-file              Default configuration SQL dump file.\n"
00422          " -q[qqq]                  Quiet mode, -q omits debug, -qq debug and"
00423          "info,\n                          "
00424          "-qqq omit debug, info and warn, -qqqq no output\n"
00425          " -l level                 Set log level directly mutually exclusive"
00426          "with -q,\n                          "
00427          "level is one of debug, info, warn, error, or none\n"
00428          " -L loggers               Define loggers. By default this setting is"
00429          "read from\n                          "
00430          "config (console logger if unset). Format is:\n"
00431          "                          logger:args[;logger2:args2[!...]]\n"
00432          "                          Currently supported:\n"
00433          "                          console, file:file.log, network logger always added\n"
00434          " -p plugins               List of plugins to load on startup in given order\n"
00435          " -P port                  TCP port to listen on for Fawkes network connections.\n"
00436          " --net-service-name=name  mDNS service name to use.\n"
00437          " -u user                  Drop privileges and run as given user.\n"
00438          " -g group                 Drop privileges and run as given group.\n"
00439 #ifdef HAVE_LIBDAEMON
00440          " -D[pid file]             Run daemonized in the background, pid file "
00441          "is optional,\n                          "
00442          "default is /var/run/fawkes.pid, must be absolute path.\n"
00443          " -D[pid file] -k          Kill a daemonized Fawkes running in the"
00444          "background\n"
00445          " -D[pid file] -s          Check status of daemon.\n"
00446 #endif
00447          "\n", progname);
00448 }
00449 
00450 
00451 } // end namespace runtime
00452 } // end namespace fawkes