Fawkes API
Fawkes Development Version
|
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