dtnd.cc

Go to the documentation of this file.
00001 /*
00002  *    Copyright 2004-2006 Intel Corporation
00003  * 
00004  *    Licensed under the Apache License, Version 2.0 (the "License");
00005  *    you may not use this file except in compliance with the License.
00006  *    You may obtain a copy of the License at
00007  * 
00008  *        http://www.apache.org/licenses/LICENSE-2.0
00009  * 
00010  *    Unless required by applicable law or agreed to in writing, software
00011  *    distributed under the License is distributed on an "AS IS" BASIS,
00012  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  *    See the License for the specific language governing permissions and
00014  *    limitations under the License.
00015  */
00016 
00017 
00018 #include <fcntl.h>
00019 #include <errno.h>
00020 #include <string>
00021 #include <sys/time.h>
00022 
00023 #include <oasys/debug/FatalSignals.h>
00024 #include <oasys/debug/Log.h>
00025 #include <oasys/io/NetUtils.h>
00026 #include <oasys/memory/Memory.h>
00027 #include <oasys/tclcmd/ConsoleCommand.h>
00028 #include <oasys/tclcmd/TclCommand.h>
00029 #include <oasys/thread/Timer.h>
00030 #include <oasys/util/Getopt.h>
00031 #include <oasys/util/Random.h>
00032 #include <oasys/util/StringBuffer.h>
00033 
00034 #include "applib/APIServer.h"
00035 #include "cmd/TestCommand.h"
00036 #include "servlib/DTNServer.h"
00037 #include "storage/DTNStorageConfig.h"
00038 
00039 extern const char* dtn_version;
00040 
00044 namespace dtn {
00045 
00049 class DTND {
00050 public:
00051     DTND();
00052     int main(int argc, char* argv[]);
00053 
00054 protected:
00055     bool                  daemonize_;
00056     int                   daemonize_pipe_[2];
00057     int                   random_seed_;
00058     bool                  random_seed_set_;
00059     std::string           conf_file_;
00060     bool                  conf_file_set_;
00061     bool                  print_version_;
00062     std::string           loglevelstr_;
00063     oasys::log_level_t    loglevel_;
00064     std::string           logfile_;
00065     TestCommand*          testcmd_;
00066     oasys::ConsoleCommand* consolecmd_;
00067     DTNStorageConfig      storage_config_;
00068 
00069     void get_options(int argc, char* argv[]);
00070     void daemonize();
00071     void notify_parent(char status);
00072     void notify_and_exit(char status);
00073     void seed_random();
00074     void init_log();
00075     void init_testcmd(int argc, char* argv[]);
00076     void run_console();
00077 };
00078 
00079 //----------------------------------------------------------------------
00080 DTND::DTND()
00081     : daemonize_(false),
00082       random_seed_(0),
00083       random_seed_set_(false),
00084       conf_file_(""),
00085       conf_file_set_(false),
00086       print_version_(false),
00087       loglevelstr_(""),
00088       loglevel_(LOG_DEFAULT_THRESHOLD),
00089       logfile_("-"),
00090       testcmd_(NULL),
00091       consolecmd_(NULL),
00092       storage_config_("storage",        // command name
00093                       "berkeleydb",     // storage type
00094                       "DTN",            // DB name
00095                       "/var/lib/dtn/db")        // DB directory
00096 {
00097     // override defaults from oasys storage config
00098     storage_config_.db_max_tx_ = 1000;
00099 
00100     daemonize_pipe_[0] = -1;
00101     daemonize_pipe_[1] = -1;
00102 
00103     testcmd_    = new TestCommand();
00104     consolecmd_ = new oasys::ConsoleCommand("dtn% ");
00105 }
00106 
00107 //----------------------------------------------------------------------
00108 void
00109 DTND::get_options(int argc, char* argv[])
00110 {
00111     
00112     // Register all command line options
00113     oasys::Getopt opts;
00114     opts.addopt(
00115         new oasys::BoolOpt('v', "version", &print_version_,
00116                            "print version information and exit"));
00117 
00118     opts.addopt(
00119         new oasys::StringOpt('o', "output", &logfile_, "<output>",
00120                              "file name for logging output "
00121                              "(default - indicates stdout)"));
00122 
00123     opts.addopt(
00124         new oasys::StringOpt('l', NULL, &loglevelstr_, "<level>",
00125                              "default log level [debug|warn|info|crit]"));
00126 
00127     opts.addopt(
00128         new oasys::StringOpt('c', "conf", &conf_file_, "<conf>",
00129                              "set the configuration file", &conf_file_set_));
00130     opts.addopt(
00131         new oasys::BoolOpt('d', "daemonize", &daemonize_,
00132                            "run as a daemon"));
00133 
00134     opts.addopt(
00135         new oasys::BoolOpt('t', "tidy", &storage_config_.tidy_,
00136                            "clear database and initialize tables on startup"));
00137 
00138     opts.addopt(
00139         new oasys::BoolOpt(0, "init-db", &storage_config_.init_,
00140                            "initialize database on startup"));
00141 
00142     opts.addopt(
00143         new oasys::IntOpt('s', "seed", &random_seed_, "<seed>",
00144                           "random number generator seed", &random_seed_set_));
00145 
00146     opts.addopt(
00147         new oasys::InAddrOpt(0, "console-addr", &consolecmd_->addr_, "<addr>",
00148                              "set the console listening addr (default off)"));
00149     
00150     opts.addopt(
00151         new oasys::UInt16Opt(0, "console-port", &consolecmd_->port_, "<port>",
00152                              "set the console listening port (default off)"));
00153     
00154     opts.addopt(
00155         new oasys::IntOpt('i', 0, &testcmd_->id_, "<id>",
00156                           "set the test id"));
00157     
00158     int remainder = opts.getopt(argv[0], argc, argv);
00159     if (remainder != argc) 
00160     {
00161         fprintf(stderr, "invalid argument '%s'\n", argv[remainder]);
00162         opts.usage("dtnd");
00163         exit(1);
00164     }
00165 }
00166 
00167 //----------------------------------------------------------------------
00168 void
00169 DTND::daemonize()
00170 {
00171     if (!daemonize_) {
00172         return;
00173     }
00174        
00175     /*
00176      * If we're running as a daemon, we fork the parent process as
00177      * soon as possible, and in particular, before TCL has been
00178      * initialized.
00179      *
00180      * Then, the parent waits on a pipe for the child to notify it as
00181      * to whether or not the initialization succeeded.
00182      */
00183     fclose(stdin);
00184     
00185     if (pipe(daemonize_pipe_) != 0) {
00186         fprintf(stderr, "error creating pipe for daemonize process: %s",
00187                 strerror(errno));
00188         exit(1);
00189     }
00190 
00191     pid_t pid = fork();
00192     if (pid == -1) {
00193         fprintf(stderr, "error forking daemon process: %s",
00194                 strerror(errno));
00195         exit(1);
00196     }
00197 
00198     if (pid > 0) {
00199         // the parent closes the write half of the pipe, then waits
00200         // for the child to return its status on the read half
00201         close(daemonize_pipe_[1]);
00202         
00203         char status;
00204         int count = read(daemonize_pipe_[0], &status, 1);
00205         if (count != 1) {
00206             fprintf(stderr, "error reading from daemon pipe: %s",
00207                     strerror(errno));
00208             exit(1);
00209         }
00210 
00211         close(daemonize_pipe_[1]);
00212         exit(status);
00213 
00214     } else {
00215         // the child continues on in a new session, closing the
00216         // unneeded read half of the pipe
00217         close(daemonize_pipe_[0]);
00218         setsid();
00219     }
00220 }
00221 
00222 //----------------------------------------------------------------------
00223 void
00224 DTND::notify_parent(char status)
00225 {
00226     if (daemonize_) {
00227         write(daemonize_pipe_[1], &status, 1);
00228         close(daemonize_pipe_[1]);
00229     }
00230 }
00231 
00232 //----------------------------------------------------------------------
00233 void
00234 DTND::notify_and_exit(char status)
00235 {
00236     notify_parent(status);
00237     exit(status);
00238 }
00239 
00240 //----------------------------------------------------------------------
00241 void
00242 DTND::seed_random()
00243 {
00244     // seed the random number generator
00245     if (!random_seed_set_) 
00246     {
00247         struct timeval tv;
00248         gettimeofday(&tv, NULL);
00249         random_seed_ = tv.tv_usec;
00250     }
00251     
00252     log_notice_p("/dtnd", "random seed is %u\n", random_seed_);
00253     oasys::Random::seed(random_seed_);
00254 }
00255 
00256 //----------------------------------------------------------------------
00257 void
00258 DTND::init_log()
00259 {
00260     // Parse the debugging level argument
00261     if (loglevelstr_.length() == 0) 
00262     {
00263         loglevel_ = oasys::LOG_NOTICE;
00264     }
00265     else 
00266     {
00267         loglevel_ = oasys::str2level(loglevelstr_.c_str());
00268         if (loglevel_ == oasys::LOG_INVALID) 
00269         {
00270             fprintf(stderr, "invalid level value '%s' for -l option, "
00271                     "expected debug | info | warning | error | crit\n",
00272                     loglevelstr_.c_str());
00273             notify_and_exit(1);
00274         }
00275     }
00276     oasys::Log::init(logfile_.c_str(), loglevel_, "", "~/.dtndebug");
00277     oasys::Log::instance()->add_reparse_handler(SIGHUP);
00278     oasys::Log::instance()->add_rotate_handler(SIGUSR1);
00279 
00280     if (daemonize_) {
00281         if (logfile_ == "-") {
00282             fprintf(stderr, "daemon mode requires setting of -o <logfile>\n");
00283             notify_and_exit(1);
00284         }
00285         
00286         oasys::Log::instance()->redirect_stdio();
00287     }
00288 }
00289 
00290 //----------------------------------------------------------------------
00291 void
00292 DTND::init_testcmd(int argc, char* argv[])
00293 {
00294     for (int i = 0; i < argc; ++i) {
00295         testcmd_->argv_.append(argv[i]);
00296         testcmd_->argv_.append(" ");
00297     }
00298 
00299     testcmd_->bind_vars();
00300     oasys::TclCommandInterp::instance()->reg(testcmd_);
00301 }
00302 
00303 //----------------------------------------------------------------------
00304 void
00305 DTND::run_console()
00306 {
00307     // launch the console server
00308     if (consolecmd_->port_ != 0) {
00309         log_info_p("/dtnd", "starting console on %s:%d",
00310                    intoa(consolecmd_->addr_), consolecmd_->port_);
00311         
00312         oasys::TclCommandInterp::instance()->
00313             command_server(consolecmd_->prompt_.c_str(),
00314                            consolecmd_->addr_, consolecmd_->port_);
00315     }
00316     
00317     if (daemonize_ || (consolecmd_->stdio_ == false)) {
00318         oasys::TclCommandInterp::instance()->event_loop();
00319     } else {
00320         oasys::TclCommandInterp::instance()->
00321             command_loop(consolecmd_->prompt_.c_str());
00322     }
00323 }
00324 
00325 //----------------------------------------------------------------------
00326 int
00327 DTND::main(int argc, char* argv[])
00328 {
00329 #ifdef OASYS_DEBUG_MEMORY_ENABLED
00330     oasys::DbgMemInfo::init();
00331 #endif
00332 
00333     get_options(argc, argv);
00334 
00335     if (print_version_) 
00336     {
00337         printf("%s\n", dtn_version);
00338         exit(0);
00339     }
00340 
00341     daemonize();
00342 
00343     init_log();
00344     
00345     log_notice_p("/dtnd", "DTN daemon starting up... (pid %d)", getpid());
00346     oasys::FatalSignals::init("dtnd");
00347 
00348     if (oasys::TclCommandInterp::init(argv[0]) != 0)
00349     {
00350         log_crit_p("/dtnd", "Can't init TCL");
00351         notify_and_exit(1);
00352     }
00353 
00354     seed_random();
00355 
00356     // stop thread creation b/c of initialization dependencies
00357     oasys::Thread::activate_start_barrier();
00358 
00359     DTNServer* dtnserver = new DTNServer("/dtnd", &storage_config_);
00360     APIServer* apiserver = new APIServer();
00361 
00362     dtnserver->init();
00363 
00364     oasys::TclCommandInterp::instance()->reg(consolecmd_);
00365     init_testcmd(argc, argv);
00366 
00367     if (! dtnserver->parse_conf_file(conf_file_, conf_file_set_)) {
00368         log_err_p("/dtnd", "error in configuration file, exiting...");
00369         notify_and_exit(1);
00370     }
00371 
00372     if (storage_config_.init_)
00373     {
00374         log_notice_p("/dtnd", "initializing persistent data store");
00375     }
00376 
00377     if (! dtnserver->init_datastore()) {
00378         log_err_p("/dtnd", "error initializing data store, exiting...");
00379         notify_and_exit(1);
00380     }
00381     
00382     // If we're running as --init-db, make an empty database and exit
00383     if (storage_config_.init_ && !storage_config_.tidy_)
00384     {
00385         dtnserver->close_datastore();
00386         log_info_p("/dtnd", "database initialization complete.");
00387         notify_and_exit(0);
00388     }
00389 
00390     // if we've daemonized, now is the time to notify our parent
00391     // process that we've successfully initialized
00392     notify_parent(0);
00393     
00394     dtnserver->start();
00395     apiserver->bind_listen_start(apiserver->local_addr(), 
00396                                  apiserver->local_port());
00397     oasys::Thread::release_start_barrier(); // run blocked threads
00398 
00399     // if the test script specified something to run for the test,
00400     // then execute it now
00401     if (testcmd_->initscript_.length() != 0) {
00402         oasys::TclCommandInterp::instance()->
00403             exec_command(testcmd_->initscript_.c_str());
00404     }
00405 
00406     // allow startup messages to be flushed to standard-out before
00407     // the prompt is displayed
00408     oasys::Thread::yield();
00409     usleep(500000);
00410     
00411     run_console();
00412 
00413     log_notice_p("/dtnd", "command loop exited... shutting down daemon");
00414     oasys::TclCommandInterp::shutdown();
00415     dtnserver->shutdown();
00416     
00417     // close out servers
00418     delete dtnserver;
00419     delete apiserver;
00420     
00421     // give other threads (like logging) a moment to catch up before exit
00422     oasys::Thread::yield();
00423     sleep(1);
00424     
00425     // kill logging
00426     oasys::Log::shutdown();
00427     
00428     return 0;
00429 }
00430 
00431 } // namespace dtn
00432 
00433 int
00434 main(int argc, char* argv[])
00435 {
00436     dtn::DTND dtnd;
00437     dtnd.main(argc, argv);
00438 }

Generated on Thu Jun 7 12:54:26 2007 for DTN Reference Implementation by  doxygen 1.5.1