Fawkes API
Fawkes Development Version
|
00001 00002 /*************************************************************************** 00003 * main.cpp - Fawkes main application 00004 * 00005 * Created: Thu Nov 2 16:44:48 2006 00006 * Copyright 2006-2008 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 <mainapp/main_thread.h> 00025 #include <utils/system/signal.h> 00026 #include <utils/system/argparser.h> 00027 #include <core/threading/mutex.h> 00028 #include <core/threading/mutex_locker.h> 00029 00030 #include <iostream> 00031 #include <cstdlib> 00032 #include <cstdio> 00033 #include <unistd.h> 00034 #include <pwd.h> 00035 #include <grp.h> 00036 #include <sys/types.h> 00037 #ifdef HAVE_LIBDAEMON 00038 # include <cerrno> 00039 # include <cstring> 00040 # include <libdaemon/dfork.h> 00041 # include <libdaemon/dlog.h> 00042 # include <libdaemon/dpid.h> 00043 # include <sys/stat.h> 00044 # include <sys/wait.h> 00045 #endif 00046 00047 using namespace std; 00048 using namespace fawkes; 00049 00050 /** Fawkes main application. 00051 * 00052 * @author Tim Niemueller 00053 */ 00054 class FawkesMainApp : public SignalHandler 00055 { 00056 public: 00057 00058 /** Constructor. */ 00059 FawkesMainApp() 00060 { 00061 __init_running = true; 00062 __init_quit = false; 00063 __sigint_running = false; 00064 } 00065 00066 /** Run main thread. 00067 * @param argp argument parser 00068 */ 00069 void run(ArgumentParser *argp) 00070 { 00071 try { 00072 fmt = new FawkesMainThread(argp); 00073 } catch (Exception &e) { 00074 throw; 00075 } 00076 00077 __init_mutex.lock(); 00078 __init_running = false; 00079 if ( ! __init_quit ) { 00080 fmt->start(); 00081 __init_mutex.unlock(); 00082 fmt->join(); 00083 } else { 00084 __init_mutex.unlock(); 00085 } 00086 00087 delete fmt; 00088 } 00089 00090 /** Handle signals. 00091 * @param signum signal number 00092 */ 00093 void handle_signal(int signum) 00094 { 00095 if ((signum == SIGINT) && ! __sigint_running) { 00096 printf("\nFawkes: SIGINT received, shutting down.\n" 00097 "Hit Ctrl-C again to force immediate exit.\n\n"); 00098 MutexLocker lock(&__init_mutex); 00099 if (__init_running) { 00100 __init_quit = true; 00101 } else { 00102 fmt->cancel(); 00103 } 00104 __sigint_running = true; 00105 } else if ((signum == SIGTERM) || __sigint_running) { 00106 // we really need to quit 00107 exit(-2); 00108 } 00109 } 00110 00111 private: 00112 FawkesMainThread *fmt; 00113 Mutex __init_mutex; 00114 bool __init_running; 00115 bool __init_quit; 00116 bool __sigint_running; 00117 }; 00118 00119 00120 void 00121 usage(const char *progname) 00122 { 00123 cout << "Fawkes Main Application - Usage Instructions" << endl 00124 << "===============================================================================" << endl 00125 << "Call with: " << progname << " [options]" << endl 00126 << "where [options] is one or more of:" << endl 00127 << " -h These help instructions" << endl 00128 << " -C Cleanup old BB segments" << endl 00129 << " -c db-file Mutable configuration file, created if it does not exist" << endl 00130 << " if it does however it must contain valid SQLite database" << endl 00131 << " -d sql-file Default configuration SQL dump file." << endl 00132 << " -q[qqq] Quiet mode, -q omits debug, -qq debug and info," << endl 00133 << " -qqq omit debug, info and warn, -qqqq no output of logger" << endl 00134 << " -l level Set log level directly mutually exclusive with -q" << endl 00135 << " level is one of debug, info, warn, error and none" << endl 00136 << " -L loggers Define loggers. By default this setting is read from " << endl 00137 << " config file (or console logger if unset in config)." << endl 00138 << " format for loggers is: logger:args[;logger2:args2[!...]]" << endl 00139 << " the loggeroptions depend on the logger. Currently supported:" << endl 00140 << " console (default), file:file.log, network logger always starts" << endl 00141 << " -p plugins Comma-separated list of plugins, for example " << endl 00142 << " fvbase,fvfountain,fvretriever. These plugins will be loaded" << endl 00143 << " in the given order after startup." << endl 00144 << " -u user Drop privileges as soon as possible and run as given user." << endl 00145 << " -g group Drop privileges as soon as possible and run as given group." << endl 00146 #ifdef HAVE_LIBDAEMON 00147 << " -D[pid file] Run daemonized in the background, pid file is optional, " << endl 00148 << " defaults to /var/run/fawkes.pid, must be absolute path." << endl 00149 << " -D[pid file] -k Kill a daemonized process running in the background," << endl 00150 << " pid file is optional as above." << endl 00151 << " -D[pid file] -s Check status of daemon." << endl 00152 #endif 00153 << endl; 00154 } 00155 00156 00157 #ifdef HAVE_LIBDAEMON 00158 void 00159 daemonize_cleanup() 00160 { 00161 daemon_retval_send(-1); 00162 daemon_retval_done(); 00163 daemon_pid_file_remove(); 00164 } 00165 00166 pid_t 00167 daemonize(int argc, char **argv) 00168 { 00169 pid_t pid; 00170 mode_t old_umask = umask(0); 00171 00172 // Prepare for return value passing 00173 daemon_retval_init(); 00174 00175 // Do the fork 00176 if ((pid = daemon_fork()) < 0) { 00177 return -1; 00178 00179 } else if (pid) { // the parent 00180 int ret; 00181 00182 // Wait for 20 seconds for the return value passed from the daemon process 00183 if ((ret = daemon_retval_wait(20)) < 0) { 00184 daemon_log(LOG_ERR, "Could not recieve return value from daemon process."); 00185 return -1; 00186 } 00187 00188 if ( ret != 0 ) { 00189 daemon_log(LOG_ERR, "*** Daemon startup failed, see syslog for details. ***"); 00190 switch (ret) { 00191 case 1: 00192 daemon_log(LOG_ERR, "Daemon failed to close file descriptors"); 00193 break; 00194 case 2: 00195 daemon_log(LOG_ERR, "Daemon failed to create PID file"); 00196 break; 00197 } 00198 return -1; 00199 } else { 00200 return pid; 00201 } 00202 00203 } else { // the daemon 00204 #ifdef DAEMON_CLOSE_ALL_AVAILABLE 00205 if (daemon_close_all(-1) < 0) { 00206 daemon_log(LOG_ERR, "Failed to close all file descriptors: %s", strerror(errno)); 00207 // Send the error condition to the parent process 00208 daemon_retval_send(1); 00209 return -1; 00210 } 00211 #endif 00212 00213 // Create the PID file 00214 if (daemon_pid_file_create() < 0) { 00215 printf("Could not create PID file (%s).", strerror(errno)); 00216 daemon_log(LOG_ERR, "Could not create PID file (%s).", strerror(errno)); 00217 00218 // Send the error condition to the parent process 00219 daemon_retval_send(2); 00220 return -1; 00221 } 00222 00223 // Send OK to parent process 00224 daemon_retval_send(0); 00225 00226 daemon_log(LOG_INFO, "Sucessfully started"); 00227 00228 umask(old_umask); 00229 return 0; 00230 } 00231 } 00232 00233 /** Global variable containing the path to the PID file. 00234 * unfortunately needed for libdaemon */ 00235 const char *fawkes_pid_file; 00236 00237 /** Function that returns the PID file name. 00238 * @return PID file name 00239 */ 00240 const char * 00241 fawkes_daemon_pid_file_proc() 00242 { 00243 return fawkes_pid_file; 00244 } 00245 #endif // HAVE_LIBDAEMON 00246 00247 /** Fawkes application. 00248 * @param argc argument count 00249 * @param argv array of arguments 00250 */ 00251 int 00252 main(int argc, char **argv) 00253 { 00254 ArgumentParser *argp = NULL; 00255 try { 00256 argp = new ArgumentParser(argc, argv, "hCc:d:q::l:L:p:D::ksu:g:"); 00257 } 00258 catch (UnknownArgumentException &e) { 00259 cout << endl; 00260 usage(argv[0]); 00261 delete argp; 00262 return -1; 00263 } 00264 00265 // default user/group 00266 const char *user = NULL; 00267 const char *group = NULL; 00268 if (argp->has_arg("u")) { 00269 user = argp->arg("u"); 00270 } 00271 if (argp->has_arg("g")) { 00272 group = argp->arg("g"); 00273 } 00274 00275 #ifdef HAVE_LIBDAEMON 00276 pid_t pid; 00277 int ret; 00278 00279 if ( argp->has_arg("D") ) { 00280 // Set identification string for the daemon for both syslog and PID file 00281 daemon_pid_file_ident = daemon_log_ident = daemon_ident_from_argv0(argv[0]); 00282 if ( argp->arg("D") != NULL ) { 00283 fawkes_pid_file = argp->arg("D"); 00284 daemon_pid_file_proc = fawkes_daemon_pid_file_proc; 00285 } 00286 00287 // We should daemonize, check if we were called to kill a daemonized copy 00288 if ( argp->has_arg("k") ) { 00289 // Check that the daemon is not run twice a the same time 00290 if ((pid = daemon_pid_file_is_running()) < 0) { 00291 daemon_log(LOG_ERR, "Fawkes daemon not running."); 00292 return 1; 00293 } 00294 00295 // Kill daemon with SIGINT 00296 if ((ret = daemon_pid_file_kill_wait(SIGINT, 5)) < 0) { 00297 daemon_log(LOG_WARNING, "Failed to kill daemon"); 00298 } 00299 return (ret < 0) ? 1 : 0; 00300 } 00301 00302 if ( argp->has_arg("s") ) { 00303 // Check daemon status 00304 return (daemon_pid_file_is_running() < 0); 00305 } 00306 00307 // Check that the daemon is not run twice a the same time 00308 if ((pid = daemon_pid_file_is_running()) >= 0) { 00309 daemon_log(LOG_ERR, "Daemon already running on (PID %u)", pid); 00310 return 201; 00311 } 00312 00313 pid = daemonize(argc, argv); 00314 if ( pid < 0 ) { 00315 daemonize_cleanup(); 00316 return 201; 00317 } else if (pid) { 00318 // parent 00319 return 0; 00320 } // else child, continue as usual 00321 } 00322 #else 00323 if ( argp->has_arg("D") ) { 00324 printf("Daemonizing support is not available.\n" 00325 "(libdaemon[-devel] was not available at compile time)\n"); 00326 return 202; 00327 } 00328 #endif 00329 00330 if (user != NULL) { 00331 struct passwd *pw; 00332 if (! (pw = getpwnam(user))) { 00333 printf("Failed to find user %s, check -u argument.\n", user); 00334 return 203; 00335 } 00336 int r = 0; 00337 r = setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid); 00338 if (r < 0) { 00339 perror("Failed to drop privileges (user)"); 00340 } 00341 } 00342 00343 if (group != NULL) { 00344 struct group *gr; 00345 if (! (gr = getgrnam(group))) { 00346 printf("Failed to find group %s, check -g argument.\n", user); 00347 return 204; 00348 } 00349 int r = 0; 00350 r = setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid); 00351 if (r < 0) { 00352 perror("Failed to drop privileges (group)"); 00353 } 00354 } 00355 00356 Thread::init_main(); 00357 00358 if ( argp->has_arg("h") ) { 00359 usage(argv[0]); 00360 delete argp; 00361 return 0; 00362 } 00363 00364 FawkesMainApp fawkes; 00365 SignalManager::register_handler(SIGINT, &fawkes); 00366 SignalManager::register_handler(SIGTERM, &fawkes); 00367 00368 try { 00369 fawkes.run(argp); 00370 } catch (Exception &e) { 00371 printf("Running Fawkes failed\n"); 00372 e.print_trace(); 00373 } 00374 00375 Thread::destroy_main(); 00376 00377 #ifdef HAVE_LIBDAEMON 00378 if ( argp->has_arg("D") ) { 00379 daemonize_cleanup(); 00380 } 00381 #endif 00382 00383 delete argp; 00384 return 0; 00385 }