Fawkes API  Fawkes Development Version
run.cpp
1 
2 /***************************************************************************
3  * run.cpp - Fawkes run functions
4  *
5  * Created: Wed May 04 23:23:23 2011
6  * Copyright 2006-2011 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. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 #include <baseapp/run.h>
25 #include <baseapp/daemonize.h>
26 #include <baseapp/main_thread.h>
27 #include <baseapp/thread_manager.h>
28 
29 #include <core/threading/thread.h>
30 
31 #include <blackboard/local.h>
32 #include <config/sqlite.h>
33 #include <config/net_handler.h>
34 #include <utils/ipc/shm.h>
35 #include <utils/system/argparser.h>
36 #include <logging/multi.h>
37 #include <logging/console.h>
38 #include <logging/liblogger.h>
39 #include <logging/factory.h>
40 #include <logging/network.h>
41 #include <utils/time/clock.h>
42 #include <netcomm/fawkes/network_manager.h>
43 #include <plugin/manager.h>
44 #include <plugin/net/handler.h>
45 #include <aspect/manager.h>
46 
47 
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <pwd.h>
51 #include <grp.h>
52 #include <cstdlib>
53 #include <cstdio>
54 #include <cstring>
55 #include <unistd.h>
56 #include <signal.h>
57 
58 namespace fawkes {
59  namespace runtime {
60 #if 0 /* just to make Emacs auto-indent happy */
61  }
62 }
63 #endif
64 
65 ArgumentParser * argument_parser = NULL;
66 FawkesMainThread * main_thread = NULL;
67 MultiLogger * logger = NULL;
68 NetworkLogger * network_logger = NULL;
69 BlackBoard * blackboard = NULL;
70 SQLiteConfiguration * config = NULL;
71 PluginManager * plugin_manager = NULL;
72 AspectManager * aspect_manager = NULL;
73 ThreadManager * thread_manager = NULL;
74 FawkesNetworkManager * network_manager = NULL;
75 ConfigNetworkHandler * nethandler_config = NULL;
76 PluginNetworkHandler * nethandler_plugin = NULL;
77 Clock * clock = NULL;
78 SharedMemoryRegistry * shm_registry;
79 InitOptions * init_options = NULL;
80 
81 // this is NOT shared to the outside
82 FawkesMainThread::Runner * runner = NULL;
83 
84 int
85 init(int argc, char **argv)
86 {
87  return init(InitOptions(argc, argv));
88 }
89 
90 
91 int
92 init(InitOptions options)
93 {
94  init_options = new InitOptions(options);
95 
96  if (init_options->show_help()) return 0;
97 
98  if ( options.daemonize() ) {
99  fawkes::daemon::init(options.daemon_pid_file(), options.basename());
100  if (options.daemonize_kill()) {
101  fawkes::daemon::kill();
102  } else if (options.daemonize_status()) {
103  return fawkes::daemon::running() ? 0 : 1;
104  } else {
105  fawkes::daemon::start();
106  }
107  }
108 
109  // *** set user group if requested
110  const char *user = NULL;
111  const char *group = NULL;
112  if (options.has_username()) {
113  user = options.username();
114  }
115  if (options.has_groupname()) {
116  group = options.groupname();
117  }
118 
119  if (user != NULL) {
120  struct passwd *pw;
121  if (! (pw = getpwnam(user))) {
122  printf("Failed to find user %s, check -u argument.\n", user);
123  return 203;
124  }
125  int r = 0;
126  r = setreuid(pw->pw_uid, pw->pw_uid);
127  if (r < 0) {
128  perror("Failed to drop privileges (user)");
129  }
130  }
131 
132  if (group != NULL) {
133  struct group *gr;
134  if (! (gr = getgrnam(group))) {
135  printf("Failed to find group %s, check -g argument.\n", user);
136  return 204;
137  }
138  int r = 0;
139  r = setregid(gr->gr_gid, gr->gr_gid);
140  if (r < 0) {
141  perror("Failed to drop privileges (group)");
142  }
143  }
144 
145  // *** setup base thread and shm registry
147  shm_registry = new SharedMemoryRegistry(true);
148 
149  // *** setup logging
150  if (options.has_loggers()) {
151  try {
152  logger = LoggerFactory::multilogger_instance(options.loggers());
153  } catch (Exception &e) {
154  e.append("Initializing multi logger failed");
155  throw;
156  }
157  } else {
158  logger = new MultiLogger(new ConsoleLogger());
159  }
160 
161  logger->set_loglevel(options.log_level());
162  LibLogger::init(logger);
163 
164  // *** Prepare home dir directory, just in case
165  const char *homedir = getenv("HOME");
166  if (homedir) {
167  char *userdir;
168  if (asprintf(&userdir, "%s/%s", homedir, USERDIR) != -1) {
169  if (access(userdir, W_OK) != 0) {
170  if (mkdir(userdir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1) {
171  logger->log_warn("FawkesMainThread", "Failed to create .fawkes "
172  "directory %s, trying without", userdir);
173  }
174  }
175  free(userdir);
176  }
177  }
178 
179  // *** setup config
180  config = new SQLiteConfiguration(CONFDIR);
181 
182  config->load(options.host_config(), options.default_config());
183 
184  try {
185  SQLiteConfiguration::SQLiteValueIterator *i = config->modified_iterator();
186  while (i->next()) {
187  std::string modtype = i->get_modtype();
188  if (modtype == "changed") {
189  logger->log_warn("FawkesMainThread", "Default config value CHANGED: %s"
190  "(was: %s now: %s)", i->path(),
191  i->get_oldvalue().c_str(), i->get_as_string().c_str());
192  } else if (modtype == "erased") {
193  logger->log_warn("FawkesMainThread", "Default config value ERASED: %s",
194  i->path());
195  } else {
196  logger->log_debug("FawkesMainThread", "Default config value ADDED: %s "
197  "(value: %s)", i->path(), i->get_as_string().c_str());
198  }
199  }
200  delete i;
201  } catch (Exception &e) {
202  logger->log_warn("FawkesMainThread", "Failed to read modified default "
203  "config values, no dump?");
204  }
205 
206 
207  // *** Determine network parameters
208  unsigned int net_tcp_port = 1910;
209  std::string net_service_name = "Fawkes on %h";
210  if (options.has_net_tcp_port()) {
211  net_tcp_port = options.net_tcp_port();
212  } else {
213  try {
214  net_tcp_port = config->get_uint("/fawkes/mainapp/net/tcp_port");
215  } catch (Exception &e) {} // ignore, we stick with the default
216  }
217 
218  if (options.has_net_service_name()) {
219  net_service_name = options.net_service_name();
220  } else {
221  try {
222  net_service_name = config->get_string("/fawkes/mainapp/net/service_name");
223  } catch (Exception &e) {} // ignore, we stick with the default
224  }
225 
226  if (net_tcp_port > 65535) {
227  logger->log_warn("FawkesMainThread", "Invalid port '%u', using 1910",
228  net_tcp_port);
229  net_tcp_port = 1910;
230  }
231 
232  // *** Setup blackboard
233  std::string bb_magic_token = "";
234  unsigned int bb_size = 2097152;
235  try {
236  bb_magic_token = config->get_string("/fawkes/mainapp/blackboard_magic_token");
237  logger->log_info("FawkesMainApp", "BlackBoard magic token defined. "
238  "Using shared memory BlackBoard.");
239  } catch (Exception &e) {
240  // ignore
241  }
242  try {
243  bb_size = config->get_uint("/fawkes/mainapp/blackboard_size");
244  } catch (Exception &e) {
245  logger->log_warn("FawkesMainApp", "BlackBoard size not defined. "
246  "Will use %u, saving to default DB", bb_size);
247  config->set_default_uint("/fawkes/mainapp/blackboard_size", bb_size);
248  }
249 
250  // Cleanup stale BlackBoard shared memory segments if requested
251  if ( options.bb_cleanup()) {
252  LocalBlackBoard::cleanup(bb_magic_token.c_str(),
253  /* output with lister? */ true);
255  }
256 
257  LocalBlackBoard *lbb = NULL;
258  if ( bb_magic_token == "") {
259  lbb = new LocalBlackBoard(bb_size);
260  } else {
261  lbb = new LocalBlackBoard(bb_size, bb_magic_token.c_str());
262  }
263  blackboard = lbb;
264 
265  aspect_manager = new AspectManager();
266  thread_manager = new ThreadManager(aspect_manager, aspect_manager);
267 
268  plugin_manager = new PluginManager(thread_manager, config,
269  "/fawkes/meta_plugins/",
270  options.plugin_module_flags(),
271  options.init_plugin_cache());
272  network_manager = new FawkesNetworkManager(thread_manager,
273  net_tcp_port,
274  net_service_name.c_str());
275  nethandler_config = new ConfigNetworkHandler(config,
276  network_manager->hub());
277 
278  nethandler_plugin = new PluginNetworkHandler(plugin_manager,
279  network_manager->hub());
280  nethandler_plugin->start();
281 
282  network_logger = new NetworkLogger(network_manager->hub(),
283  logger->loglevel());
284  logger->add_logger(network_logger);
285 
286  clock = Clock::instance();
287 
288  lbb->start_nethandler(network_manager->hub());
289 
290 
291  // *** Create main thread, but do not start, yet
292  main_thread = new fawkes::FawkesMainThread(config, logger,
293  thread_manager,
294  plugin_manager,
295  options.load_plugin_list(),
296  options.default_plugin());
297 
298  aspect_manager->register_default_inifins(blackboard,
299  thread_manager->aspect_collector(),
300  config, logger, clock,
301  network_manager->hub(),
302  main_thread, logger,
303  thread_manager,
304  network_manager->nnresolver(),
305  network_manager->service_publisher(),
306  network_manager->service_browser(),
307  plugin_manager);
308 
309 
310 
311  return 0;
312 }
313 
314 void
315 cleanup()
316 {
317  if (init_options->daemonize()) {
318  fawkes::daemon::cleanup();
319  }
320 
321  if (nethandler_plugin) {
322  nethandler_plugin->cancel();
323  nethandler_plugin->join();
324  }
325 
326  if (logger) {
327  // Must delete network logger first since network manager
328  // has to die before the LibLogger is finalized.
329  logger->remove_logger(network_logger);
330  delete network_logger;
331  }
332 
333  delete main_thread;
334  delete argument_parser;
335  delete init_options;
336  delete nethandler_config;
337  delete nethandler_plugin;
338  delete plugin_manager;
339  delete network_manager;
340  delete config;
341  delete thread_manager;
342  delete aspect_manager;
343  delete shm_registry;
344 
345  main_thread = NULL;
346  argument_parser = NULL;
347  init_options = NULL;
348  nethandler_config = NULL;
349  nethandler_plugin = NULL;
350  plugin_manager = NULL;
351  network_manager = NULL;
352  config = NULL;
353  thread_manager = NULL;
354  aspect_manager = NULL;
355  shm_registry = NULL;
356 
357  // implicitly frees multi_logger and all sub-loggers
359  logger = NULL;
360 
361  Clock::finalize();
362  clock = NULL;
363 
364  try {
366  } catch (Exception &e) {} // ignored, can fire on show_help
367 
368  // should be last, because of not disabled this hosts the
369  // default signal handlers
370  delete runner;
371  runner = 0;
372 }
373 
374 void
375 run()
376 {
377  if (init_options->show_help()) {
378  print_usage(init_options->basename());
379  return;
380  }
381 
382  bool defsigs = init_options->default_signal_handlers();
383  runner = new FawkesMainThread::Runner(main_thread, defsigs);
384 
385  try {
386  runner->run();
387  } catch (Exception &e) {
388  printf("Running Fawkes failed\n");
389  e.print_trace();
390  }
391 }
392 
393 
394 /** Quit Fawkes.
395  * You can call this from within Fawkes to quit Fawkes. Use with extreme care an
396  * only rarely.
397  * This sends SIGINT to the local process. This triggers the quit routine but also
398  * takes a currently running init into account. This is prone to the same potential
399  * problems as a SIGINT received otherwise, e.g. a never-ending thread blocking
400  * the main thread from cancelling.
401  */
402 void
403 quit()
404 {
405  kill(getpid(), SIGINT);
406 }
407 
408 void
409 print_usage(const char *progname)
410 {
411  printf("Fawkes Main Application - Usage Instructions\n"
412  "================================================"
413  "===============================\n"
414  "Usage: %s [options]\n"
415  "where [options] is one or more of:\n"
416  " -h These help instructions\n"
417  " -C Cleanup old BB and shared memory segments\n"
418  " -c db-file Mutable configuration file, created if it "
419  "does not\n "
420  "exist, if it does must contain valid SQLite database\n"
421  " -d sql-file Default configuration SQL dump file.\n"
422  " -q[qqq] Quiet mode, -q omits debug, -qq debug and"
423  "info,\n "
424  "-qqq omit debug, info and warn, -qqqq no output\n"
425  " -l level Set log level directly mutually exclusive"
426  "with -q,\n "
427  "level is one of debug, info, warn, error, or none\n"
428  " -L loggers Define loggers. By default this setting is"
429  "read from\n "
430  "config (console logger if unset). Format is:\n"
431  " logger:args[;logger2:args2[!...]]\n"
432  " Currently supported:\n"
433  " console, file:file.log, network logger always added\n"
434  " -p plugins List of plugins to load on startup in given order\n"
435  " -P port TCP port to listen on for Fawkes network connections.\n"
436  " --net-service-name=name mDNS service name to use.\n"
437  " -u user Drop privileges and run as given user.\n"
438  " -g group Drop privileges and run as given group.\n"
439 #ifdef HAVE_LIBDAEMON
440  " -D[pid file] Run daemonized in the background, pid file "
441  "is optional,\n "
442  "default is /var/run/fawkes.pid, must be absolute path.\n"
443  " -D[pid file] -k Kill a daemonized Fawkes running in the"
444  "background\n"
445  " -D[pid file] -s Check status of daemon.\n"
446 #endif
447  "\n", progname);
448 }
449 
450 
451 } // end namespace runtime
452 } // end namespace fawkes
static void finalize()
Delete internal logger.
Definition: liblogger.cpp:83
virtual std::string get_string(const char *path)
Get value from configuration which is of type string.
Definition: sqlite.cpp:1164
static MultiLogger * multilogger_instance(const char *as)
Create MultiLogger instance.
Definition: factory.cpp:121
static Clock * instance()
Clock initializer.
Definition: clock.cpp:65
ThreadCollector * aspect_collector() const
Get a thread collector to be used for an aspect initializer.
static void cleanup(const char *magic_token, bool use_lister=false)
Cleanup orphaned BlackBoard segments.
Definition: local.cpp:177
Fawkes library namespace.
virtual unsigned int get_uint(const char *path)
Get value from configuration which is of type unsigned int.
Definition: sqlite.cpp:1104
std::string get_modtype() const
Get modification type.
Definition: sqlite.cpp:2244
static void init_main()
Initialize Thread wrapper instance for main thread.
Definition: thread.cpp:1248
InitOptions & default_signal_handlers(bool enable)
Set default signal handlers.
virtual void load(const char *filename, const char *defaults_filename, const char *tag=NULL)
Load configuration.
Definition: sqlite.cpp:611
virtual void set_loglevel(LogLevel level)
Sets the log level.
Definition: multi.cpp:145
static void destroy_main()
Destroy main thread wrapper instance.
Definition: thread.cpp:1262
virtual void log_warn(const char *component, const char *format,...)
Log warning message.
Definition: multi.cpp:225
InitOptions & show_help(bool show_help)
Set to show help.
void add_logger(Logger *logger)
Add a logger.
Definition: multi.cpp:115
static void finalize()
Finalize.
Definition: clock.cpp:77
virtual void log_info(const char *component, const char *format,...)
Log informational message.
Definition: multi.cpp:203
static void init(MultiLogger *multi_logger=NULL)
Initialize logger.
Definition: liblogger.cpp:63
void run()
Run main thread.
void register_default_inifins(BlackBoard *blackboard, ThreadCollector *collector, Configuration *config, Logger *logger, Clock *clock, FawkesNetworkHub *fnethub, MainLoopEmployer *mloop_employer, LoggerEmployer *logger_employer, BlockedTimingExecutor *btexec, NetworkNameResolver *nnresolver, ServicePublisher *service_publisher, ServiceBrowser *service_browser, PluginManager *pmanager)
Register default aspect initializer/finalizer.
Definition: manager.cpp:225
virtual void set_default_uint(const char *path, unsigned int uint)
Set new default value in configuration of type unsigned int.
Definition: sqlite.cpp:1631
virtual LogLevel loglevel()
Get log level.
Definition: logger.cpp:254
Fawkes default main thread.
Definition: main_thread.h:58
virtual void log_debug(const char *component, const char *format,...)
Log debug message.
Definition: multi.cpp:181
const char * basename() const
Get program basename.
void start(bool wait=true)
Call this method to start the thread.
Definition: thread.cpp:507
InitOptions & daemonize(bool daemonize, bool kill=false, bool status=false, const char *pid_file=0)
Set daemonization options.
static void cleanup(const char *name=0)
Cleanup existing shared memory segments.
SQLiteValueIterator * modified_iterator()
Iterator for modified values.
Definition: sqlite.cpp:1979