00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
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",
00114 "berkeleydb",
00115 "DTN",
00116 "/var/lib/dtn/db")
00117 {
00118
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
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
00201
00202
00203
00204
00205
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
00224
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
00240
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
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
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
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
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
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
00415
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();
00422
00423
00424
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
00437 NOTREACHED;
00438
00439 return 0;
00440 }
00441
00442 }
00443
00444 int
00445 main(int argc, char* argv[])
00446 {
00447 dtn::DTND dtnd;
00448 dtnd.main(argc, argv);
00449 }