dtnd.cc

Go to the documentation of this file.
00001 /*
00002  * IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. By
00003  * downloading, copying, installing or using the software you agree to
00004  * this license. If you do not agree to this license, do not download,
00005  * install, copy or use the software.
00006  * 
00007  * Intel Open Source License 
00008  * 
00009  * Copyright (c) 2004 Intel Corporation. All rights reserved. 
00010  * 
00011  * Redistribution and use in source and binary forms, with or without
00012  * modification, are permitted provided that the following conditions are
00013  * met:
00014  * 
00015  *   Redistributions of source code must retain the above copyright
00016  *   notice, this list of conditions and the following disclaimer.
00017  * 
00018  *   Redistributions in binary form must reproduce the above copyright
00019  *   notice, this list of conditions and the following disclaimer in the
00020  *   documentation and/or other materials provided with the distribution.
00021  * 
00022  *   Neither the name of the Intel Corporation nor the names of its
00023  *   contributors may be used to endorse or promote products derived from
00024  *   this software without specific prior written permission.
00025  *  
00026  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00027  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00028  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00029  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR
00030  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00031  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00032  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00033  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00034  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00035  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00036  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00037  */
00038 
00039 #include <fcntl.h>
00040 #include <errno.h>
00041 #include <string>
00042 #include <sys/time.h>
00043 
00044 #include <oasys/debug/FatalSignals.h>
00045 #include <oasys/debug/Log.h>
00046 #include <oasys/io/NetUtils.h>
00047 #include <oasys/memory/Memory.h>
00048 #include <oasys/storage/StorageConfig.h>
00049 #include <oasys/tclcmd/ConsoleCommand.h>
00050 #include <oasys/tclcmd/TclCommand.h>
00051 #include <oasys/thread/Timer.h>
00052 #include <oasys/util/Getopt.h>
00053 #include <oasys/util/Random.h>
00054 #include <oasys/util/StringBuffer.h>
00055 
00056 #include "applib/APIServer.h"
00057 #include "cmd/TestCommand.h"
00058 #include "servlib/DTNServer.h"
00059 
00060 extern const char* dtn_version;
00061 
00065 namespace dtn {
00066 
00070 class DTND {
00071 public:
00072     DTND();
00073     int main(int argc, char* argv[]);
00074 
00075 protected:
00076     bool                  daemonize_;
00077     int                   daemonize_pipe_[2];
00078     int                   random_seed_;
00079     bool                  random_seed_set_;
00080     std::string           conf_file_;
00081     bool                  conf_file_set_;
00082     bool                  print_version_;
00083     std::string           loglevelstr_;
00084     oasys::log_level_t    loglevel_;
00085     std::string           logfile_;
00086     TestCommand*          testcmd_;
00087     oasys::ConsoleCommand* consolecmd_;
00088     oasys::StorageConfig  storage_config_;
00089 
00090     void get_options(int argc, char* argv[]);
00091     void daemonize();
00092     void notify_parent(char status);
00093     void notify_and_exit(char status);
00094     void seed_random();
00095     void init_log();
00096     void init_testcmd(int argc, char* argv[]);
00097     void run_console();
00098 };
00099 
00100 //----------------------------------------------------------------------
00101 DTND::DTND()
00102     : daemonize_(false),
00103       random_seed_(0),
00104       random_seed_set_(false),
00105       conf_file_(""),
00106       conf_file_set_(false),
00107       print_version_(false),
00108       loglevelstr_(""),
00109       loglevel_(LOG_DEFAULT_THRESHOLD),
00110       logfile_("-"),
00111       testcmd_(NULL),
00112       consolecmd_(NULL),
00113       storage_config_("storage",        // command name
00114                       "berkeleydb",     // storage type
00115                       "DTN",            // DB name
00116                       "/var/lib/dtn/db")        // DB directory
00117 {
00118     // override defaults from oasys storage config
00119     storage_config_.db_max_tx_ = 1000;
00120 
00121     daemonize_pipe_[0] = -1;
00122     daemonize_pipe_[1] = -1;
00123 
00124     testcmd_    = new TestCommand();
00125     consolecmd_ = new oasys::ConsoleCommand("dtn% ");
00126 }
00127 
00128 //----------------------------------------------------------------------
00129 void
00130 DTND::get_options(int argc, char* argv[])
00131 {
00132     
00133     // Register all command line options
00134     oasys::Getopt::addopt(
00135         new oasys::BoolOpt('v', "version", &print_version_,
00136                            "print version information and exit"));
00137 
00138     oasys::Getopt::addopt(
00139         new oasys::StringOpt('o', "output", &logfile_, "<output>",
00140                              "file name for logging output "
00141                              "(default - indicates stdout)"));
00142 
00143     oasys::Getopt::addopt(
00144         new oasys::StringOpt('l', NULL, &loglevelstr_, "<level>",
00145                              "default log level [debug|warn|info|crit]"));
00146 
00147     oasys::Getopt::addopt(
00148         new oasys::StringOpt('c', "conf", &conf_file_, "<conf>",
00149                              "set the configuration file", &conf_file_set_));
00150     oasys::Getopt::addopt(
00151         new oasys::BoolOpt('d', "daemonize", &daemonize_,
00152                            "run as a daemon"));
00153 
00154     oasys::Getopt::addopt(
00155         new oasys::BoolOpt('t', "tidy", &storage_config_.tidy_,
00156                            "clear database and initialize tables on startup"));
00157 
00158     oasys::Getopt::addopt(
00159         new oasys::BoolOpt(0, "init-db", &storage_config_.init_,
00160                            "initialize database on startup"));
00161 
00162     oasys::Getopt::addopt(
00163         new oasys::IntOpt('s', "seed", &random_seed_, "<seed>",
00164                           "random number generator seed", &random_seed_set_));
00165 
00166     oasys::Getopt::addopt(
00167         new oasys::InAddrOpt(0, "console-addr", &consolecmd_->addr_, "<addr>",
00168                              "set the console listening addr (default off)"));
00169     
00170     oasys::Getopt::addopt(
00171         new oasys::UInt16Opt(0, "console-port", &consolecmd_->port_, "<port>",
00172                              "set the console listening port (default off)"));
00173     
00174     oasys::Getopt::addopt(
00175         new oasys::IntOpt('i', 0, &testcmd_->id_, "<id>",
00176                           "set the test id"));
00177     
00178     oasys::Getopt::addopt(
00179         new oasys::BoolOpt('f', 0, &testcmd_->fork_,
00180                            "test scripts should fork child daemons"));
00181 
00182     int remainder = oasys::Getopt::getopt(argv[0], argc, argv);
00183     if (remainder != argc) 
00184     {
00185         fprintf(stderr, "invalid argument '%s'\n", argv[remainder]);
00186         oasys::Getopt::usage("dtnd");
00187         exit(1);
00188     }
00189 }
00190 
00191 //----------------------------------------------------------------------
00192 void
00193 DTND::daemonize()
00194 {
00195     if (!daemonize_) {
00196         return;
00197     }
00198        
00199     /*
00200      * If we're running as a daemon, we fork the parent process as
00201      * soon as possible, and in particular, before TCL has been
00202      * initialized.
00203      *
00204      * Then, the parent waits on a pipe for the child to notify it as
00205      * to whether or not the initialization succeeded.
00206      */
00207     fclose(stdin);
00208     
00209     if (pipe(daemonize_pipe_) != 0) {
00210         fprintf(stderr, "error creating pipe for daemonize process: %s",
00211                 strerror(errno));
00212         exit(1);
00213     }
00214 
00215     pid_t pid = fork();
00216     if (pid == -1) {
00217         fprintf(stderr, "error forking daemon process: %s",
00218                 strerror(errno));
00219         exit(1);
00220     }
00221 
00222     if (pid > 0) {
00223         // the parent closes the write half of the pipe, then waits
00224         // for the child to return its status on the read half
00225         close(daemonize_pipe_[1]);
00226         
00227         char status;
00228         int count = read(daemonize_pipe_[0], &status, 1);
00229         if (count != 1) {
00230             fprintf(stderr, "error reading from daemon pipe: %s",
00231                     strerror(errno));
00232             exit(1);
00233         }
00234 
00235         close(daemonize_pipe_[1]);
00236         exit(status);
00237 
00238     } else {
00239         // the child continues on in a new session, closing the
00240         // unneeded read half of the pipe
00241         close(daemonize_pipe_[0]);
00242         setsid();
00243     }
00244 }
00245 
00246 //----------------------------------------------------------------------
00247 void
00248 DTND::notify_parent(char status)
00249 {
00250     if (daemonize_) {
00251         write(daemonize_pipe_[1], &status, 1);
00252         close(daemonize_pipe_[1]);
00253     }
00254 }
00255 
00256 //----------------------------------------------------------------------
00257 void
00258 DTND::notify_and_exit(char status)
00259 {
00260     notify_parent(status);
00261     exit(status);
00262 }
00263 
00264 //----------------------------------------------------------------------
00265 void
00266 DTND::seed_random()
00267 {
00268     // seed the random number generator
00269     if (!random_seed_set_) 
00270     {
00271         struct timeval tv;
00272         gettimeofday(&tv, NULL);
00273         random_seed_ = tv.tv_usec;
00274     }
00275     
00276     log_notice("/dtnd", "random seed is %u\n", random_seed_);
00277     oasys::Random::seed(random_seed_);
00278 }
00279 
00280 //----------------------------------------------------------------------
00281 void
00282 DTND::init_log()
00283 {
00284     // Parse the debugging level argument
00285     if (loglevelstr_.length() == 0) 
00286     {
00287         loglevel_ = oasys::LOG_NOTICE;
00288     }
00289     else 
00290     {
00291         loglevel_ = oasys::str2level(loglevelstr_.c_str());
00292         if (loglevel_ == oasys::LOG_INVALID) 
00293         {
00294             fprintf(stderr, "invalid level value '%s' for -l option, "
00295                     "expected debug | info | warning | error | crit\n",
00296                     loglevelstr_.c_str());
00297             notify_and_exit(1);
00298         }
00299     }
00300     oasys::Log::init(logfile_.c_str(), loglevel_, "", "~/.dtndebug");
00301     oasys::Log::instance()->add_reparse_handler(SIGHUP);
00302     oasys::Log::instance()->add_rotate_handler(SIGUSR1);
00303 
00304     if (daemonize_) {
00305         if (logfile_ == "-") {
00306             fprintf(stderr, "daemon mode requires setting of -o <logfile>\n");
00307             notify_and_exit(1);
00308         }
00309         
00310         oasys::Log::instance()->redirect_stdio();
00311     }
00312 }
00313 
00314 //----------------------------------------------------------------------
00315 void
00316 DTND::init_testcmd(int argc, char* argv[])
00317 {
00318     for (int i = 0; i < argc; ++i) {
00319         testcmd_->argv_.append(argv[i]);
00320         testcmd_->argv_.append(" ");
00321     }
00322 
00323     testcmd_->bind_vars();
00324     oasys::TclCommandInterp::instance()->reg(testcmd_);
00325 }
00326 
00327 //----------------------------------------------------------------------
00328 void
00329 DTND::run_console()
00330 {
00331     // launch the console server
00332     if (consolecmd_->port_ != 0) {
00333         log_info("/dtnd", "starting console on %s:%d",
00334                  intoa(consolecmd_->addr_), consolecmd_->port_);
00335         
00336         oasys::TclCommandInterp::instance()->
00337             command_server(consolecmd_->prompt_.c_str(),
00338                            consolecmd_->addr_, consolecmd_->port_);
00339     }
00340     
00341     if (daemonize_ || (consolecmd_->stdio_ == false)) {
00342         oasys::TclCommandInterp::instance()->event_loop();
00343     } else {
00344         oasys::TclCommandInterp::instance()->
00345             command_loop(consolecmd_->prompt_.c_str());
00346     }
00347 }
00348 
00349 //----------------------------------------------------------------------
00350 int
00351 DTND::main(int argc, char* argv[])
00352 {
00353 #ifdef OASYS_DEBUG_MEMORY_ENABLED
00354     oasys::DbgMemInfo::init();
00355 #endif
00356 
00357     get_options(argc, argv);
00358 
00359     if (print_version_) 
00360     {
00361         printf("%s\n", dtn_version);
00362         exit(0);
00363     }
00364 
00365     daemonize();
00366 
00367     init_log();
00368     
00369     log_notice("/dtnd", "DTN daemon starting up... (pid %d)", getpid());
00370     oasys::FatalSignals::init("dtnd");
00371     
00372     if (oasys::TclCommandInterp::init(argv[0]) != 0)
00373     {
00374         log_crit("/dtnd", "Can't init TCL");
00375         notify_and_exit(1);
00376     }
00377 
00378     seed_random();
00379 
00380     // stop thread creation b/c of initialization dependencies
00381     oasys::Thread::activate_start_barrier();
00382 
00383     DTNServer* dtnserver = new DTNServer("/dtnd", &storage_config_);
00384     APIServer* apiserver = new APIServer();
00385 
00386     dtnserver->init();
00387 
00388     oasys::TclCommandInterp::instance()->reg(consolecmd_);
00389     init_testcmd(argc, argv);
00390 
00391     if (! dtnserver->parse_conf_file(conf_file_, conf_file_set_)) {
00392         log_err("/dtnd", "error in configuration file, exiting...");
00393         notify_and_exit(1);
00394     }
00395 
00396     if (storage_config_.init_)
00397     {
00398         log_notice("/dtnd", "initializing persistent data store");
00399     }
00400 
00401     if (! dtnserver->init_datastore()) {
00402         log_err("/dtnd", "error initializing data store, exiting...");
00403         notify_and_exit(1);
00404     }
00405     
00406     // If we're running as --init-db, make an empty database and exit
00407     if (storage_config_.init_ && !storage_config_.tidy_)
00408     {
00409         dtnserver->close_datastore();
00410         log_info("/dtnd", "database initialization complete.");
00411         notify_and_exit(0);
00412     }
00413 
00414     // if we've daemonized, now is the time to notify our parent
00415     // process that we've successfully initialized
00416     notify_parent(0);
00417     
00418     dtnserver->start();
00419     apiserver->bind_listen_start(apiserver->local_addr(), 
00420                                  apiserver->local_port());
00421     oasys::Thread::release_start_barrier(); // run blocked threads
00422 
00423     // if the test script specified something to run for the test,
00424     // then execute it now
00425     if (testcmd_->initscript_.length() != 0) {
00426         oasys::TclCommandInterp::instance()->
00427             exec_command(testcmd_->initscript_.c_str());
00428     }
00429 
00430     run_console();
00431 
00432     log_info("/dtnd", "command loop exited... shutting down daemon");
00433     oasys::TclCommandInterp::shutdown();
00434     dtnserver->shutdown();
00435 
00436     // doesn't return
00437     NOTREACHED;
00438 
00439     return 0;
00440 }
00441 
00442 } // namespace dtn
00443 
00444 int
00445 main(int argc, char* argv[])
00446 {
00447     dtn::DTND dtnd;
00448     dtnd.main(argc, argv);
00449 }

Generated on Fri Dec 22 14:47:58 2006 for DTN Reference Implementation by  doxygen 1.5.1