main.cpp

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 conffile      Mutable configuration file, created if it does not exist" << endl
00130        << "                  if it does however it must contain valid SQLite database" << endl
00131        << " -d conffile      Default configuration file, created if it does not exist" << endl
00132        << "                  if it does however it must contain valid SQLite database" << endl
00133        << " -q[qqq]          Quiet mode, -q omits debug, -qq debug and info," << endl
00134        << "                  -qqq omit debug, info and warn, -qqqq no output of logger" << endl
00135        << " -l level         Set log level directly mutually exclusive with -q" << endl
00136        << "                  level is one of debug, info, warn, error and none" << endl
00137        << " -L loggers       Define loggers. By default this setting is read from " << endl
00138        << "                  config file (or console logger if unset in config)." << endl
00139        << "                  format for loggers is: logger:args[;logger2:args2[!...]]" << endl
00140        << "                  the loggeroptions depend on the logger. Currently supported:" << endl
00141        << "                  console (default), file:file.log, network logger always starts" << endl
00142        << " -p plugins       Comma-separated list of plugins, for example " << endl
00143        << "                  fvbase,fvfountain,fvretriever. These plugins will be loaded" << endl
00144        << "                  in the given order after startup." << endl
00145        << " -u user          Drop privileges as soon as possible and run as given user." << endl
00146        << " -g group         Drop privileges as soon as possible and run as given group." << endl
00147 #ifdef HAVE_LIBDAEMON
00148        << " -D[pid file]     Run daemonized in the background, pid file is optional, " << endl
00149        << "                  defaults to /var/run/fawkes.pid, must be absolute path." << endl
00150        << " -D[pid file] -k  Kill a daemonized process running in the background," << endl
00151        << "                  pid file is optional as above." << endl
00152        << " -D[pid file] -s  Check status of daemon." << endl
00153 #endif
00154        << endl;
00155 }
00156
00157
00158 #ifdef HAVE_LIBDAEMON
00159 void
00160 daemonize_cleanup()
00161 {
00162   daemon_retval_send(-1);
00163   daemon_retval_done();
00164   daemon_pid_file_remove();
00165 }
00166
00167 pid_t
00168 daemonize(int argc, char **argv)
00169 {
00170   pid_t pid;
00171   mode_t old_umask = umask(0);
00172
00173   // Prepare for return value passing
00174   daemon_retval_init();
00175
00176   // Do the fork
00177   if ((pid = daemon_fork()) < 0) {
00178     return -1;
00179
00180   } else if (pid) { // the parent
00181     int ret;
00182
00183     // Wait for 20 seconds for the return value passed from the daemon process
00184     if ((ret = daemon_retval_wait(20)) < 0) {
00185       daemon_log(LOG_ERR, "Could not recieve return value from daemon process.");
00186       return -1;
00187     }
00188
00189     if ( ret != 0 ) {
00190       daemon_log(LOG_ERR, "*** Daemon startup failed, see syslog for details. ***");
00191       switch (ret) {
00192       case 1:
00193         daemon_log(LOG_ERR, "Daemon failed to close file descriptors");
00194         break;
00195       case 2:
00196         daemon_log(LOG_ERR, "Daemon failed to create PID file");
00197         break;
00198       }
00199       return -1;
00200     } else {
00201       return pid;
00202     }
00203
00204   } else { // the daemon
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
00212     // Create the PID file
00213     if (daemon_pid_file_create() < 0) {
00214       printf("Could not create PID file (%s).", strerror(errno));
00215       daemon_log(LOG_ERR, "Could not create PID file (%s).", strerror(errno));
00216
00217       // Send the error condition to the parent process
00218       daemon_retval_send(2);
00219       return -1;
00220     }
00221
00222     // Send OK to parent process
00223     daemon_retval_send(0);
00224
00225     daemon_log(LOG_INFO, "Sucessfully started");
00226
00227     umask(old_umask);
00228     return 0;
00229   }
00230 }
00231 
00232 /** Global variable containing the path to the PID file.
00233  * unfortunately needed for libdaemon */
00234 const char *fawkes_pid_file;
00235 
00236 /** Function that returns the PID file name.
00237  * @return PID file name
00238  */
00239 const char *
00240 fawkes_daemon_pid_file_proc()
00241 {
00242   return fawkes_pid_file;
00243 }
00244 #endif // HAVE_LIBDAEMON
00245 
00246 /** Fawkes application.
00247  * @param argc argument count
00248  * @param argv array of arguments
00249  */
00250 int
00251 main(int argc, char **argv)
00252 {
00253   ArgumentParser *argp = new ArgumentParser(argc, argv, "hCc:d:q::l:L:p:D::ksu:g:");
00254
00255   // default user/group
00256   const char *user  = NULL;
00257   const char *group = NULL;
00258   if (argp->has_arg("u")) {
00259     user = argp->arg("u");
00260   }
00261   if (argp->has_arg("g")) {
00262     group = argp->arg("g");
00263   }
00264
00265 #ifdef HAVE_LIBDAEMON
00266   pid_t pid;
00267   int ret;
00268
00269   if ( argp->has_arg("D") ) {
00270     // Set identification string for the daemon for both syslog and PID file
00271     daemon_pid_file_ident = daemon_log_ident = daemon_ident_from_argv0(argv[0]);
00272     if ( argp->arg("D") != NULL ) {
00273       fawkes_pid_file      = argp->arg("D");
00274       daemon_pid_file_proc = fawkes_daemon_pid_file_proc;
00275     }
00276
00277     // We should daemonize, check if we were called to kill a daemonized copy
00278     if ( argp->has_arg("k") ) {
00279       // Check that the daemon is not run twice a the same time
00280       if ((pid = daemon_pid_file_is_running()) < 0) {
00281         daemon_log(LOG_ERR, "Fawkes daemon not running.");
00282         return 1;
00283       }
00284
00285       // Kill daemon with SIGINT
00286       if ((ret = daemon_pid_file_kill_wait(SIGINT, 5)) < 0) {
00287         daemon_log(LOG_WARNING, "Failed to kill daemon");
00288       }
00289       return (ret < 0) ? 1 : 0;
00290     }
00291
00292     if ( argp->has_arg("s") ) {
00293       // Check daemon status
00294       return (daemon_pid_file_is_running() < 0);
00295     }
00296
00297     // Check that the daemon is not run twice a the same time
00298     if ((pid = daemon_pid_file_is_running()) >= 0) {
00299       daemon_log(LOG_ERR, "Daemon already running on (PID %u)", pid);
00300       return 201;
00301     }
00302
00303     pid = daemonize(argc, argv);
00304     if ( pid < 0 ) {
00305       daemonize_cleanup();
00306       return 201;
00307     } else if (pid) {
00308       // parent
00309       return 0;
00310     } // else child, continue as usual
00311   }
00312 #else
00313   if ( argp->has_arg("D") ) {
00314     printf("Daemonizing support is not available.\n"
00315            "(libdaemon[-devel] was not available at compile time)\n");
00316     return 202;
00317   }
00318 #endif
00319 
00320   if (user != NULL) {
00321     struct passwd *pw;
00322     if (! (pw = getpwnam(user))) {
00323       printf("Failed to find user %s, check -u argument.\n", user);
00324       return 203;
00325     }
00326     int r = 0;
00327     r = setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid);
00328     if (r < 0) {
00329       perror("Failed to drop privileges (user)");
00330     }
00331   }
00332
00333   if (group != NULL) {
00334     struct group *gr;
00335     if (! (gr = getgrnam(group))) {
00336       printf("Failed to find group %s, check -g argument.\n", user);
00337       return 204;
00338     }
00339     int r = 0;
00340     r = setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid);
00341     if (r < 0) {
00342       perror("Failed to drop privileges (group)");
00343     }
00344   }
00345
00346   Thread::init_main();
00347
00348   if ( argp->has_arg("h") ) {
00349     usage(argv[0]);
00350     delete argp;
00351     return 0;
00352   }
00353
00354   FawkesMainApp fawkes;
00355   SignalManager::register_handler(SIGINT, &fawkes);
00356   SignalManager::register_handler(SIGTERM, &fawkes);
00357
00358   try {
00359     fawkes.run(argp);
00360   } catch (Exception &e) {
00361     printf("Running Fawkes failed\n");
00362     e.print_trace();
00363   }
00364
00365   Thread::destroy_main();
00366
00367 #ifdef HAVE_LIBDAEMON
00368   if ( argp->has_arg("D") ) {
00369     daemonize_cleanup();
00370   }
00371 #endif
00372 
00373   delete argp;
00374   return 0;
00375 }